diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 79ee123c2b..82c2e2f0bb 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e57f7daf01..e5be05ea9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,7 @@ buildscript { allprojects { group = "hu.bme.mit.theta" - version = "6.6.6" + version = "6.7.0" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } diff --git a/subprojects/cfa/cfa-analysis/src/main/kotlin/hu/bme/mit/theta/cfa/analysis/CfaToMonolithicExpr.kt b/subprojects/cfa/cfa-analysis/src/main/kotlin/hu/bme/mit/theta/cfa/analysis/CfaToMonolithicExpr.kt index ca6c125314..0ba48342d9 100644 --- a/subprojects/cfa/cfa-analysis/src/main/kotlin/hu/bme/mit/theta/cfa/analysis/CfaToMonolithicExpr.kt +++ b/subprojects/cfa/cfa-analysis/src/main/kotlin/hu/bme/mit/theta/cfa/analysis/CfaToMonolithicExpr.kt @@ -13,76 +13,80 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package hu.bme.mit.theta.cfa.analysis -package hu.bme.mit.theta.cfa.analysis; - -import com.google.common.base.Preconditions; +import com.google.common.base.Preconditions import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr import hu.bme.mit.theta.analysis.expl.ExplState -import hu.bme.mit.theta.analysis.ptr.PtrState -import hu.bme.mit.theta.cfa.CFA; -import hu.bme.mit.theta.core.decl.Decls; -import hu.bme.mit.theta.core.model.ImmutableValuation +import hu.bme.mit.theta.cfa.CFA +import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.model.Valuation import hu.bme.mit.theta.core.stmt.* import hu.bme.mit.theta.core.type.booltype.BoolExprs.And import hu.bme.mit.theta.core.type.inttype.IntExprs.* import hu.bme.mit.theta.core.type.inttype.IntLitExpr -import hu.bme.mit.theta.core.utils.StmtUtils; -import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; +import hu.bme.mit.theta.core.utils.StmtUtils +import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory import java.util.* fun CFA.toMonolithicExpr(): MonolithicExpr { - Preconditions.checkArgument(this.errorLoc.isPresent); + Preconditions.checkArgument(this.errorLoc.isPresent) - val map = mutableMapOf() - for ((i, x) in this.locs.withIndex()) { - map[x] = i; - } - val locVar = Decls.Var("__loc__", Int()) - val tranList = this.edges.map { e -> - SequenceStmt.of(listOf( + val map = mutableMapOf() + for ((i, x) in this.locs.withIndex()) { + map[x] = i + } + val locVar = Decls.Var("__loc__", Int()) + val tranList = + this.edges + .map { e -> + SequenceStmt.of( + listOf( AssumeStmt.of(Eq(locVar.ref, Int(map[e.source]!!))), e.stmt, - AssignStmt.of(locVar, Int(map[e.target]!!)) - )) - }.toList() - val trans = NonDetStmt.of(tranList); - val transUnfold = StmtUtils.toExpr(trans, VarIndexingFactory.indexing(0)); - val transExpr = And(transUnfold.exprs) - val initExpr = Eq(locVar.ref, Int(map[this.initLoc]!!)) - val propExpr = Neq(locVar.ref, Int(map[this.errorLoc.orElseThrow()]!!)) + AssignStmt.of(locVar, Int(map[e.target]!!)), + ) + ) + } + .toList() + val trans = NonDetStmt.of(tranList) + val transUnfold = StmtUtils.toExpr(trans, VarIndexingFactory.indexing(0)) + val transExpr = And(transUnfold.exprs) + val initExpr = Eq(locVar.ref, Int(map[this.initLoc]!!)) + val propExpr = Neq(locVar.ref, Int(map[this.errorLoc.orElseThrow()]!!)) - val offsetIndex = transUnfold.indexing + val offsetIndex = transUnfold.indexing - return MonolithicExpr(initExpr, transExpr, propExpr, offsetIndex) + return MonolithicExpr(initExpr, transExpr, propExpr, offsetIndex) } fun CFA.valToAction(val1: Valuation, val2: Valuation): CfaAction { - val val1Map = val1.toMap() - val val2Map = val2.toMap() - var i = 0 - val map: MutableMap = mutableMapOf() - for (x in this.locs) { - map[x] = i++ + val val1Map = val1.toMap() + val val2Map = val2.toMap() + var i = 0 + val map: MutableMap = mutableMapOf() + for (x in this.locs) { + map[x] = i++ + } + return CfaAction.create( + this.edges.first { edge -> + map[edge.source] == + (val1Map[val1Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() && + map[edge.target] == + (val2Map[val2Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() } - return CfaAction.create( - this.edges.first { edge -> - map[edge.source] == (val1Map[val1Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() && - map[edge.target] == (val2Map[val2Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() - }) + ) } fun CFA.valToState(val1: Valuation): CfaState { - val valMap = val1.toMap() - var i = 0 - val map: MutableMap = mutableMapOf() - for (x in this.locs) { - map[i++] = x - } - return CfaState.of( - map[(valMap[valMap.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt()], - ExplState.of(val1) - ) + val valMap = val1.toMap() + var i = 0 + val map: MutableMap = mutableMapOf() + for (x in this.locs) { + map[i++] = x + } + return CfaState.of( + map[(valMap[valMap.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt()], + ExplState.of(val1), + ) } - diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedCheckerBuilder.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedCheckerBuilder.kt index bfd1aeadf9..642cfe1265 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedCheckerBuilder.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/bounded/BoundedCheckerBuilder.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.bounded import hu.bme.mit.theta.analysis.expr.ExprAction @@ -25,49 +24,85 @@ import hu.bme.mit.theta.solver.Solver @JvmOverloads fun buildBMC( - monolithicExpr: MonolithicExpr, - bmcSolver: Solver, - valToState: (Valuation) -> S, - biValToAction: (Valuation, Valuation) -> A, - logger: Logger, - shouldGiveUp: (Int) -> Boolean = { false }, - bmcEnabled: () -> Boolean = { true }, - lfPathOnly: () -> Boolean = { true }, + monolithicExpr: MonolithicExpr, + bmcSolver: Solver, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> A, + logger: Logger, + shouldGiveUp: (Int) -> Boolean = { false }, + bmcEnabled: () -> Boolean = { true }, + lfPathOnly: () -> Boolean = { true }, ): BoundedChecker { - return BoundedChecker(monolithicExpr, shouldGiveUp, bmcSolver, bmcEnabled, lfPathOnly, null, { false }, null, - { false }, valToState, biValToAction, logger) + return BoundedChecker( + monolithicExpr, + shouldGiveUp, + bmcSolver, + bmcEnabled, + lfPathOnly, + null, + { false }, + null, + { false }, + valToState, + biValToAction, + logger, + ) } @JvmOverloads fun buildKIND( - monolithicExpr: MonolithicExpr, - bmcSolver: Solver, - indSolver: Solver, - valToState: (Valuation) -> S, - biValToAction: (Valuation, Valuation) -> A, - logger: Logger, - shouldGiveUp: (Int) -> Boolean = { false }, - bmcEnabled: () -> Boolean = { true }, - lfPathOnly: () -> Boolean = { true }, - kindEnabled: (Int) -> Boolean = { true }, + monolithicExpr: MonolithicExpr, + bmcSolver: Solver, + indSolver: Solver, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> A, + logger: Logger, + shouldGiveUp: (Int) -> Boolean = { false }, + bmcEnabled: () -> Boolean = { true }, + lfPathOnly: () -> Boolean = { true }, + kindEnabled: (Int) -> Boolean = { true }, ): BoundedChecker { - return BoundedChecker(monolithicExpr, shouldGiveUp, bmcSolver, bmcEnabled, lfPathOnly, null, { false }, indSolver, - kindEnabled, valToState, biValToAction, logger) + return BoundedChecker( + monolithicExpr, + shouldGiveUp, + bmcSolver, + bmcEnabled, + lfPathOnly, + null, + { false }, + indSolver, + kindEnabled, + valToState, + biValToAction, + logger, + ) } @JvmOverloads fun buildIMC( - monolithicExpr: MonolithicExpr, - bmcSolver: Solver, - itpSolver: ItpSolver, - valToState: (Valuation) -> S, - biValToAction: (Valuation, Valuation) -> A, - logger: Logger, - shouldGiveUp: (Int) -> Boolean = { false }, - bmcEnabled: () -> Boolean = { true }, - lfPathOnly: () -> Boolean = { true }, - imcEnabled: (Int) -> Boolean = { true }, + monolithicExpr: MonolithicExpr, + bmcSolver: Solver, + itpSolver: ItpSolver, + valToState: (Valuation) -> S, + biValToAction: (Valuation, Valuation) -> A, + logger: Logger, + shouldGiveUp: (Int) -> Boolean = { false }, + bmcEnabled: () -> Boolean = { true }, + lfPathOnly: () -> Boolean = { true }, + imcEnabled: (Int) -> Boolean = { true }, ): BoundedChecker { - return BoundedChecker(monolithicExpr, shouldGiveUp, bmcSolver, bmcEnabled, lfPathOnly, itpSolver, imcEnabled, null, - { false }, valToState, biValToAction, logger) -} \ No newline at end of file + return BoundedChecker( + monolithicExpr, + shouldGiveUp, + bmcSolver, + bmcEnabled, + lfPathOnly, + itpSolver, + imcEnabled, + null, + { false }, + valToState, + biValToAction, + logger, + ) +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/MemoryEvent.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/MemoryEvent.kt index d10f26e4d1..6708288159 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/MemoryEvent.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/MemoryEvent.kt @@ -13,102 +13,115 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.mcm import hu.bme.mit.theta.core.decl.VarDecl - -open class MemoryEvent(protected val type: MemoryEventType, protected val tag: String, protected val id: Int) { - - fun tag(): String { - return tag +open class MemoryEvent( + protected val type: MemoryEventType, + protected val tag: String, + protected val id: Int, +) { + + fun tag(): String { + return tag + } + + enum class MemoryEventType(val label: String) { + READ("R"), + WRITE("W"), + FENCE("F"), + META("M"), + } + + fun type(): MemoryEventType { + return type + } + + open fun asRead(): Read { + throw UnsupportedOperationException("Not a read!") + } + + open fun asWrite(): Write { + throw UnsupportedOperationException("Not a write!") + } + + open fun asFence(): Fence { + throw UnsupportedOperationException("Not a fence!") + } + + open fun asMemoryIO(): MemoryIO { + throw UnsupportedOperationException("Not memory IO!") + } + + abstract class MemoryIO( + id: Int, + private val `var`: VarDecl<*>, + private val localVar: VarDecl<*>, + type: MemoryEventType, + tag: String, + ) : MemoryEvent(type, tag, id) { + + override fun asMemoryIO(): MemoryIO { + return this } - enum class MemoryEventType(val label: String) { - READ("R"), - WRITE("W"), - FENCE("F"), - META("M") + override fun toString(): String { + return type.toString() + + "{" + + "var=" + + `var` + + ", localVar=" + + localVar + + ", tag=" + + tag + + ", id=" + + id + + '}' } - fun type(): MemoryEventType { - return type + fun `var`(): VarDecl<*> { + return `var` } - open fun asRead(): Read { - throw UnsupportedOperationException("Not a read!") + fun localVar(): VarDecl<*> { + return localVar } + } - open fun asWrite(): Write { - throw UnsupportedOperationException("Not a write!") - } + class Read(id: Int, `var`: VarDecl<*>, localVar: VarDecl<*>, tag: String) : + MemoryIO(id, `var`, localVar, MemoryEventType.READ, tag) { - open fun asFence(): Fence { - throw UnsupportedOperationException("Not a fence!") + override fun asRead(): Read { + return this } - - open fun asMemoryIO(): MemoryIO { - throw UnsupportedOperationException("Not memory IO!") + } + + class Write( + id: Int, + `var`: VarDecl<*>, + localVar: VarDecl<*>, + private val dependencies: Set>, + tag: String, + ) : MemoryIO(id, `var`, localVar, MemoryEventType.WRITE, tag) { + + override fun asWrite(): Write { + return this } - abstract class MemoryIO(id: Int, private val `var`: VarDecl<*>, private val localVar: VarDecl<*>, - type: MemoryEventType, tag: String) : - MemoryEvent(type, tag, id) { - - override fun asMemoryIO(): MemoryIO { - return this - } - - override fun toString(): String { - return type.toString() + "{" + - "var=" + `var` + - ", localVar=" + localVar + - ", tag=" + tag + - ", id=" + id + - '}' - } - - fun `var`(): VarDecl<*> { - return `var` - } - - fun localVar(): VarDecl<*> { - return localVar - } + fun dependencies(): Set> { + return dependencies } + } - class Read(id: Int, `var`: VarDecl<*>, localVar: VarDecl<*>, tag: String) : - MemoryIO(id, `var`, localVar, MemoryEventType.READ, tag) { + class Fence(id: Int, tag: String) : MemoryEvent(MemoryEventType.FENCE, tag, id) { - override fun asRead(): Read { - return this - } + override fun asFence(): Fence { + return this } - class Write(id: Int, `var`: VarDecl<*>, localVar: VarDecl<*>, private val dependencies: Set>, - tag: String) : - MemoryIO(id, `var`, localVar, MemoryEventType.WRITE, tag) { - - override fun asWrite(): Write { - return this - } - - fun dependencies(): Set> { - return dependencies - } - } - - class Fence(id: Int, tag: String) : MemoryEvent(MemoryEventType.FENCE, tag, id) { - - override fun asFence(): Fence { - return this - } - - override fun toString(): String { - return type.toString() + "{" + - "tag='" + tag + '\'' + - '}' - } + override fun toString(): String { + return type.toString() + "{" + "tag='" + tag + '\'' + '}' } -} \ No newline at end of file + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/PartialSolver.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/PartialSolver.kt index a0257bb216..f894815a7e 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/PartialSolver.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/mcm/analysis/PartialSolver.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.mcm.analysis import hu.bme.mit.theta.common.Tuple @@ -23,36 +22,34 @@ import hu.bme.mit.theta.graphsolver.compilers.GraphPatternCompiler import hu.bme.mit.theta.graphsolver.patterns.constraints.GraphConstraint import hu.bme.mit.theta.graphsolver.solvers.GraphSolver -/** - * WiP solver for memory-model related tasks. - */ +/** WiP solver for memory-model related tasks. */ class PartialSolver( - private val mcm: Collection, - private val partialGraph: CandidateExecutionGraph, - private val graphPatternCompiler: GraphPatternCompiler, - private val graphPatternSolver: GraphSolver + private val mcm: Collection, + private val partialGraph: CandidateExecutionGraph, + private val graphPatternCompiler: GraphPatternCompiler, + private val graphPatternSolver: GraphSolver, ) { - fun getSolution(): CandidateExecutionGraph? { - graphPatternCompiler.addEvents(partialGraph.nodes) - graphPatternCompiler.addFacts(partialGraph.knownEvents) - - val exprs = mcm.map { it.accept(graphPatternCompiler) } - exprs.forEach { graphPatternSolver.add(it) } + fun getSolution(): CandidateExecutionGraph? { + graphPatternCompiler.addEvents(partialGraph.nodes) + graphPatternCompiler.addFacts(partialGraph.knownEvents) - val namedPatterns = mcm.map { it.collectSubRelations() }.flatten().toSet() + val exprs = mcm.map { it.accept(graphPatternCompiler) } + exprs.forEach { graphPatternSolver.add(it) } - val status = graphPatternSolver.check() + val namedPatterns = mcm.map { it.collectSubRelations() }.flatten().toSet() - return if (status.isSat) { - val (nodes, events) = graphPatternCompiler.getCompleteGraph(namedPatterns, graphPatternSolver.getModel()) - CandidateExecutionGraph(nodes, events) - } else null - } + val status = graphPatternSolver.check() + return if (status.isSat) { + val (nodes, events) = + graphPatternCompiler.getCompleteGraph(namedPatterns, graphPatternSolver.getModel()) + CandidateExecutionGraph(nodes, events) + } else null + } } data class CandidateExecutionGraph( - val nodes: List, - val knownEvents: Map, ThreeVL>) { -} \ No newline at end of file + val nodes: List, + val knownEvents: Map, ThreeVL>, +) {} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/BasicOcChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/BasicOcChecker.kt index f57d462220..706db97b38 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/BasicOcChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/BasicOcChecker.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.oc import hu.bme.mit.theta.core.decl.VarDecl @@ -25,83 +24,114 @@ import java.util.* class BasicOcChecker : OcCheckerBase() { - override val solver: Solver = SolverManager.resolveSolverFactory("Z3:4.13").createSolver() - private var relations: Array>? = null - - override fun check( - events: Map, Map>>, - pos: List>, - rfs: Map, Set>>, - ): SolverStatus? { - val modifiableRels = rfs.values.flatten() // modifiable relation vars - val flatEvents = events.values.flatMap { it.values.flatten() } - val initialRels = Array(flatEvents.size) { Array(flatEvents.size) { null } } - pos.forEach { setAndClose(initialRels, it) } - val decisionStack = Stack>() - decisionStack.push(OcAssignment(rels = initialRels)) // not really a decision point (initial) - - dpllLoop@ - while (solver.check().isSat) { // DPLL loop - val valuation = solver.model.toMap() - val changedRfs = modifiableRels.filter { rel -> - val value = rel.enabled(valuation) - decisionStack.popUntil({ it.relation == rel }, value) && value == true - } - val changedEnabledEvents = flatEvents.filter { ev -> - val enabled = ev.enabled(solver.model) - if (ev.type != EventType.WRITE || !rfs.containsKey(ev.const.varDecl)) return@filter false - decisionStack.popUntil({ it.event == ev }, enabled) && enabled == true - } + override val solver: Solver = SolverManager.resolveSolverFactory("Z3:4.13").createSolver() + private var relations: Array>? = null + + override fun check( + events: Map, Map>>, + pos: List>, + rfs: Map, Set>>, + wss: Map, Set>>, + ): SolverStatus? { + var modifiableRels = rfs.values.flatten() // modifiable relation vars + val flatEvents = events.values.flatMap { it.values.flatten() } + val initialRels = Array(flatEvents.size) { Array(flatEvents.size) { null } } + pos.forEach { setAndClose(initialRels, it) } + val decisionStack = Stack>() + decisionStack.push(OcAssignment(initialRels)) // not really a decision point (initial) + var finalWsCheck = false + + dpllLoop@ while (solver.check().isSat) { // DPLL loop + val valuation = solver.model.toMap() + val changedRels = + modifiableRels.filter { rel -> + val value = rel.enabled(valuation) + decisionStack.popUntil({ it.relation == rel }, value) && value == true + } + val changedEnabledEvents = + flatEvents.filter { ev -> + val enabled = ev.enabled(solver.model) + if (ev.type != EventType.WRITE || !rfs.containsKey(ev.const.varDecl)) return@filter false + decisionStack.popUntil({ it.event == ev }, enabled) && enabled == true + } - // propagate - for (rf in changedRfs) { - val decision = OcAssignment(decisionStack.peek().rels, rf) - decisionStack.push(decision) - val reason0 = setAndClose(decision.rels, rf) - if (propagate(reason0)) continue@dpllLoop - - val writes = events[rf.from.const.varDecl]!!.values.flatten() - .filter { it.type == EventType.WRITE && it.enabled == true } - for (w in writes) { - val reason = derive(decision.rels, rf, w) - if (propagate(reason)) continue@dpllLoop - } + // propagate + for (rel in changedRels) { + val assignment = OcAssignment(decisionStack.peek().rels, rel) + decisionStack.push(assignment) + val reason0 = setAndClose(assignment.rels, rel) + if (propagate(reason0)) continue@dpllLoop + + when (rel.type) { + RelationType.RF -> { + val writes = + events[rel.from.const.varDecl]!!.values.flatten().filter { + it.type == EventType.WRITE && it.enabled == true + } + for (w in writes) { + val reason = derive(assignment.rels, rel, w) + if (propagate(reason)) continue@dpllLoop } + } - for (w in changedEnabledEvents) { - val decision = OcAssignment(decisionStack.peek().rels, w) - decisionStack.push(decision) - for (rf in rfs[w.const.varDecl]!!.filter { it.enabled == true }) { - val reason = derive(decision.rels, rf, w) - if (propagate(reason)) continue@dpllLoop - } + RelationType.WS -> { + val matchingRfs = + rfs[rel.from.const.varDecl]?.filter { rf -> + rf.from == rel.from && decisionStack.any { it.relation == rf } + } ?: emptyList() + for (rf in matchingRfs) { + val reason = derive(assignment.rels, rf, rel.to) + if (propagate(reason)) continue@dpllLoop } + } - relations = decisionStack.peek().rels - return solver.status // no conflict found, counterexample is valid + else -> {} } + } - relations = decisionStack.peek().rels - return solver.status - } + for (w in changedEnabledEvents) { + val decision = OcAssignment(decisionStack.peek().rels, w) + decisionStack.push(decision) + for (rf in rfs[w.const.varDecl]!!.filter { it.enabled == true }) { + val reason = derive(decision.rels, rf, w) + if (propagate(reason)) continue@dpllLoop + } + } - override fun getRelations(): Array>? = relations?.copy() + if (!finalWsCheck) { + // no conflict found at this point, checking counterexample for SC + val unassignedWss = finalWsCheck(decisionStack.peek().rels, wss) + modifiableRels = modifiableRels + unassignedWss + finalWsCheck = true + continue@dpllLoop + } - override fun propagate(reason: Reason?): Boolean { - reason ?: return false - propagated.add(reason) - solver.add(BoolExprs.Not(reason.expr)) - return true + relations = decisionStack.peek().rels + return solver.status // no conflict found, counterexample is valid } - /** - * Returns true if obj is not on the stack (in other words, if the value of obj is changed in the new model) - */ - private fun Stack.popUntil(obj: (T) -> Boolean, value: Boolean?): Boolean { - val index = indexOfFirst(obj) - if (index == -1) return true - if (value == true) return false - while (size > index) pop() - return true - } -} \ No newline at end of file + relations = decisionStack.peek().rels + return solver.status + } + + override fun getRelations(): Array>? = relations?.copy() + + override fun propagate(reason: Reason?): Boolean { + reason ?: return false + propagated.add(reason) + solver.add(BoolExprs.Not(reason.expr)) + return true + } + + /** + * Returns true if obj is not on the stack (in other words, if the value of obj is changed in the + * new model) + */ + private fun Stack.popUntil(obj: (T) -> Boolean, value: Boolean?): Boolean { + val index = indexOfFirst(obj) + if (index == -1) return true + if (value == true) return false + while (size > index) pop() + return true + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcChecker.kt index 421b55f3c5..d5df1f83b9 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcChecker.kt @@ -13,186 +13,190 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.oc - import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.core.type.Expr -import hu.bme.mit.theta.core.type.booltype.BoolType -import hu.bme.mit.theta.core.type.booltype.TrueExpr +import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.solver.Solver import hu.bme.mit.theta.solver.SolverStatus internal inline fun Array>.copy(): Array> = - Array(size) { i -> Array(size) { j -> this[i][j] } } + Array(size) { i -> Array(size) { j -> this[i][j] } } /** - * This is an interface of an ordering consistency checker for concurrent systems (e.g., concurrent programs). + * This is an interface of an ordering consistency checker for concurrent systems (e.g., concurrent + * programs). * - * An ordering consistency checker takes a set an events and a set of relation between events. It checks whether - * there is an inconsistency (a cycle in the event graph based on the relations) subject to the constraints added - * to the SMT-solver. + * An ordering consistency checker takes a set an events and a set of relation between events. It + * checks whether there is an inconsistency (a cycle in the event graph based on the relations) + * subject to the constraints added to the SMT-solver. */ interface OcChecker { - val solver: Solver - - /** - * Checks the consistency of the event graph (i.e., if there is a partial order of events satisfying the given - * constraints). - * - * @param events the set of events grouped by variables - * @param pos the elements of the relation "program-order" (the relations always present based on the input model) - * @param rfs the (possible) elements of the "read-from" relation (not all of these are necessarily enabled) - * @return returns the status of the solver after running the consistency checking - */ - fun check( - events: Map, Map>>, - pos: List>, - rfs: Map, Set>> - ): SolverStatus? - - /** - * Get the discovered relations represented by their reasons between the events (or more exactly between atomic - * blocks, see Event::clkId) - */ - fun getRelations(): Array>? - - /** - * Get the list of propagated conflict clauses (their negations were added to the solver) in the form of reasons. - */ - fun getPropagatedClauses(): List + val solver: Solver + + /** + * Checks the consistency of the event graph (i.e., if there is a partial order of events + * satisfying the given constraints). + * + * @param events the set of events grouped by variables + * @param pos the elements of the relation "program-order" (the relations always present based on + * the input model) + * @param rfs the (possible) elements of the "read-from" relation (not all of these are + * necessarily enabled) + * @return returns the status of the solver after running the consistency checking + */ + fun check( + events: Map, Map>>, + pos: List>, + rfs: Map, Set>>, + wss: Map, Set>>, + ): SolverStatus? + + /** + * Get the discovered relations represented by their reasons between the events (or more exactly + * between atomic blocks, see Event::clkId) + */ + fun getRelations(): Array>? + + /** + * Get the list of propagated conflict clauses (their negations were added to the solver) in the + * form of reasons. + */ + fun getPropagatedClauses(): List } /** - * This interface implements basic utilities for an ordering consistency checker such as derivation rules and - * transitive closure operations. + * This interface implements basic utilities for an ordering consistency checker such as derivation + * rules and transitive closure operations. */ abstract class OcCheckerBase : OcChecker { - protected val propagated: MutableList = mutableListOf() + protected val propagated: MutableList = mutableListOf() - override fun getPropagatedClauses() = propagated.toList() + override fun getPropagatedClauses() = propagated.toList() - protected abstract fun propagate(reason: Reason?): Boolean + protected abstract fun propagate(reason: Reason?): Boolean - protected fun derive(rels: Array>, rf: Relation, w: E): Reason? = when { - rf.from.clkId == rf.to.clkId -> null // rf within an atomic block - w.clkId == rf.from.clkId || w.clkId == rf.to.clkId -> null // w within an atomic block with one of the rf ends + protected fun derive(rels: Array>, rf: Relation, w: E): Reason? = + when { + !rf.from.sameMemory(w) -> null // different referenced memory locations + rf.from.clkId == rf.to.clkId -> null // rf within an atomic block + w.clkId == rf.from.clkId || w.clkId == rf.to.clkId -> + null // w within an atomic block with one of the rf ends - rels[w.clkId][rf.to.clkId] != null -> { // WS derivation - val reason = WriteSerializationReason(rf, w, rels[w.clkId][rf.to.clkId]!!) - setAndClose(rels, w.clkId, rf.from.clkId, reason) - } + rels[w.clkId][rf.to.clkId] != null -> { // WS derivation + val reason = WriteSerializationReason(rf, w, rels[w.clkId][rf.to.clkId]!!) + setAndClose(rels, w.clkId, rf.from.clkId, reason) + } - rels[rf.from.clkId][w.clkId] != null -> { // FR derivation - val reason = FromReadReason(rf, w, rels[rf.from.clkId][w.clkId]!!) - setAndClose(rels, rf.to.clkId, w.clkId, reason) - } + rels[rf.from.clkId][w.clkId] != null -> { // FR derivation + val reason = FromReadReason(rf, w, rels[rf.from.clkId][w.clkId]!!) + setAndClose(rels, rf.to.clkId, w.clkId, reason) + } - else -> null + else -> null } - protected fun setAndClose(rels: Array>, rel: Relation): Reason? { - if (rel.from.clkId == rel.to.clkId) return null // within an atomic block - return setAndClose( - rels, rel.from.clkId, rel.to.clkId, - if (rel.type == RelationType.PO) PoReason else RelationReason(rel) - ) + protected fun setAndClose(rels: Array>, rel: Relation): Reason? { + if (rel.from.clkId == rel.to.clkId) return null // within an atomic block + return setAndClose( + rels, + rel.from.clkId, + rel.to.clkId, + if (rel.type == RelationType.PO) PoReason else RelationReason(rel), + ) + } + + private fun setAndClose( + rels: Array>, + from: Int, + to: Int, + reason: Reason, + ): Reason? { + if (from == to) return reason // cycle (self-loop) found + val toClose = mutableListOf(from to to to reason) + while (toClose.isNotEmpty()) { + val (fromTo, r) = toClose.removeFirst() + val (i1, i2) = fromTo + check(i1 != i2) + if (rels[i1][i2] != null) continue + + rels[i1][i2] = r + rels[i2].forEachIndexed { i2next, b -> + if (b != null && rels[i1][i2next] == null) { // i2 -> i2next, not i1 -> i2next + val combinedReason = r and b + if (i1 == i2next) return combinedReason // cycle (self-loop) found + toClose.add(i1 to i2next to combinedReason) + } + } + rels.forEachIndexed { i1previous, b -> + if ( + b[i1] != null && rels[i1previous][i2] == null + ) { // i1previous -> i1, not i1previous -> i2 + val combinedReason = r and b[i1]!! + if (i1previous == i2) return combinedReason // cycle (self-loop) found + toClose.add(i1previous to i2 to combinedReason) + } + } } - - private fun setAndClose(rels: Array>, from: Int, to: Int, reason: Reason): Reason? { - if (from == to) return reason // cycle (self-loop) found - val toClose = mutableListOf(from to to to reason) - while (toClose.isNotEmpty()) { - val (fromTo, r) = toClose.removeFirst() - val (i1, i2) = fromTo - check(i1 != i2) - if (rels[i1][i2] != null) continue - - rels[i1][i2] = r - rels[i2].forEachIndexed { i2next, b -> - if (b != null && rels[i1][i2next] == null) { // i2 -> i2next, not i1 -> i2next - val combinedReason = r and b - if (i1 == i2next) return combinedReason // cycle (self-loop) found - toClose.add(i1 to i2next to combinedReason) - } - } - rels.forEachIndexed { i1previous, b -> - if (b[i1] != null && rels[i1previous][i2] == null) { // i1previous -> i1, not i1previous -> i2 - val combinedReason = r and b[i1]!! - if (i1previous == i2) return combinedReason // cycle (self-loop) found - toClose.add(i1previous to i2 to combinedReason) - } - } + return null + } + + protected fun finalWsCheck( + rels: Array>, + wss: Map, Set>>, + ): List> { + val unassignedWss = mutableListOf>() + wss.forEach { (_, wsRels) -> + unassignedWss.addAll( + wsRels.filter { ws -> + rels[ws.from.clkId][ws.to.clkId] == null && rels[ws.to.clkId][ws.from.clkId] == null } - return null + ) } -} - -/** - * Reason(s) of an enabled relation. - */ -sealed class Reason { - - open val reasons: List get() = listOf(this) - val exprs: List> get() = toExprs() - val expr: Expr get() = exprs.toAnd() - infix fun and(other: Reason): Reason = CombinedReason(reasons + other.reasons) - open fun toExprs(): List> = reasons.map { it.toExprs() }.flatten().filter { it !is TrueExpr } - override fun hashCode(): Int = exprs.hashCode() - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Reason) return false - if (exprs != other.exprs) return false - return true + if (unassignedWss.isEmpty()) return emptyList() + + val unassignedCopy = unassignedWss.toMutableList() + val pairs = mutableListOf, Relation>>() + while (unassignedCopy.isNotEmpty()) { + val ws = unassignedCopy.removeFirst() + val pair = unassignedCopy.find { it.from == ws.to || it.to == ws.from } + if (pair != null) { + pairs.add(ws to pair) + unassignedCopy.remove(pair) + } } -} -class CombinedReason(override val reasons: List) : Reason() - -object PoReason : Reason() { - - override val reasons get() = emptyList() - override fun toExprs(): List> = listOf() -} - -class RelationReason(val relation: Relation) : Reason() { - - override fun toExprs(): List> = listOf(relation.declRef) -} - -sealed class DerivedReason(val rf: Relation, val w: Event, private val wRfRelation: Reason) : Reason() { + pairs.forEach { (ws1, ws2) -> + val wsGuard = And(ws1.from.guardExpr, ws1.to.guardExpr) + val wsCond = { ws: Relation -> Imply(ws.declRef, wsGuard) } + solver.add(wsCond(ws1)) + solver.add(wsCond(ws2)) + solver.add(Imply(wsGuard, Or(ws1.declRef, ws2.declRef))) + } - override fun toExprs(): List> = listOf(rf.declRef, w.guardExpr) + wRfRelation.toExprs() + return unassignedWss + } } -class WriteSerializationReason(rf: Relation, w: Event, wBeforeRf: Reason) : - DerivedReason(rf, w, wBeforeRf) - -class FromReadReason(rf: Relation, w: Event, wAfterRf: Reason) : - DerivedReason(rf, w, wAfterRf) - /** - * Represents the known value of an important element for ordering consistency checking. Such an important element is - * either a relation (being enabled) or an event (being enabled - having a guard that evaluates to true). - * The fix (closed by theory axioms) relations and the solver decision stack level are also stored. + * Represents the known value of an important element for ordering consistency checking. Such an + * important element is either a relation (being enabled) or an event (being enabled - having a + * guard that evaluates to true). The fix (closed by theory axioms) relations and the solver + * decision stack level are also stored. */ -class OcAssignment internal constructor( - val relation: Relation? = null, - val event: E? = null, - val rels: Array>, - val solverLevel: Int = 0, +open class OcAssignment +internal constructor( + val rels: Array>, + val relation: Relation? = null, + val event: E? = null, ) { - internal constructor(rels: Array>, e: E, solverLevel: Int = 0) - : this(event = e, rels = rels.copy(), solverLevel = solverLevel) - - internal constructor(rels: Array>, r: Relation, solverLevel: Int = 0) - : this(relation = r, rels = rels.copy(), solverLevel = solverLevel) + internal constructor(rels: Array>, e: E) : this(rels.copy(), event = e) - override fun toString(): String { - return "OcAssignment(relation=$relation, event=$event, solverLevel=$solverLevel)" - } + internal constructor( + rels: Array>, + r: Relation, + ) : this(rels.copy(), relation = r) } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcTypes.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcTypes.kt index 7b4e72b6f6..eb3d4870be 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcTypes.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcTypes.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.oc import hu.bme.mit.theta.core.decl.ConstDecl @@ -27,61 +26,155 @@ import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.booltype.BoolExprs import hu.bme.mit.theta.core.type.booltype.BoolLitExpr import hu.bme.mit.theta.core.type.booltype.BoolType +import hu.bme.mit.theta.core.type.booltype.TrueExpr -/** - * Important! Empty collection is converted to true (not false). - */ -internal fun Collection>.toAnd(): Expr = when (size) { +/** Important! Empty collection is converted to true (not false). */ +internal fun Collection>.toAnd(): Expr = + when (size) { 0 -> BoolExprs.True() 1 -> first() else -> BoolExprs.And(this) + } + +enum class EventType { + WRITE, + READ, } -enum class EventType { WRITE, READ } abstract class Event( - val const: IndexedConstDecl<*>, - val type: EventType, - var guard: Set>, - val pid: Int, - val clkId: Int + val const: IndexedConstDecl<*>, + val type: EventType, + var guard: Set>, + val pid: Int, + val clkId: Int, ) { - val guardExpr: Expr get() = guard.toAnd() - var assignment: Expr? = null - var enabled: Boolean? = null - - fun enabled(valuation: Valuation): Boolean? { - val e = try { - (guardExpr.eval(valuation) as? BoolLitExpr)?.value - } catch (e: Exception) { - null - } - enabled = e - return e - } + val guardExpr: Expr + get() = guard.toAnd() + + var assignment: Expr? = null + var enabled: Boolean? = null - override fun toString(): String { - return "Event(pid=$pid, clkId=$clkId, ${const.name}[${type.toString()[0]}], guard=$guard)" + open fun enabled(valuation: Valuation): Boolean? { + val e = tryOrNull { (guardExpr.eval(valuation) as? BoolLitExpr)?.value } + enabled = e + return e + } + + open fun sameMemory(other: Event): Boolean { + if (this === other) return true + return const.varDecl == other.const.varDecl + } + + open fun interferenceCond(other: Event): Expr? = null + + protected inline fun tryOrNull(block: () -> T?): T? = + try { + block() + } catch (e: Exception) { + null } + + override fun toString(): String { + return "Event(pid=$pid, clkId=$clkId, ${const.name}[${type.toString()[0]}], guard=$guard)" + } } -enum class RelationType { PO, RF } -data class Relation( - val type: RelationType, - val from: E, - val to: E, -) { +enum class RelationType { + PO, + RF, + WS, +} - val decl: ConstDecl = - Decls.Const("${type.toString().lowercase()}_${from.const.name}_${to.const.name}", BoolExprs.Bool()) - val declRef: RefExpr = RefExpr.of(decl) - var enabled: Boolean? = null +data class Relation(val type: RelationType, val from: E, val to: E) { - override fun toString() = - "Relation($type, ${from.const.name}[${from.type.toString()[0]}], ${to.const.name}[${to.type.toString()[0]}])" + val decl: ConstDecl = + Decls.Const( + "${type.toString().lowercase()}_${from.const.name}_${to.const.name}", + BoolExprs.Bool(), + ) + val declRef: RefExpr = RefExpr.of(decl) + var enabled: Boolean? = null - fun enabled(valuation: Map, LitExpr<*>>): Boolean? { - enabled = if (type == RelationType.PO) true else valuation[decl]?.let { (it as BoolLitExpr).value } - return enabled - } -} \ No newline at end of file + override fun toString() = + "Relation($type, ${from.const.name}[${from.type.toString()[0]}], ${to.const.name}[${to.type.toString()[0]}])" + + fun enabled(valuation: Map, LitExpr<*>>): Boolean? { + enabled = + if (type == RelationType.PO) true else valuation[decl]?.let { (it as BoolLitExpr).value } + return enabled + } +} + +/** Reason(s) of an enabled relation. */ +sealed class Reason { + + open val reasons: List + get() = listOf(this) + + protected open val expressions: List> + get() = reasons.map { it.expressions }.flatten() + + val exprs: List> + get() = expressions.filter { it !is TrueExpr } + + val expr: Expr + get() = exprs.toAnd() + + infix fun and(other: Reason): Reason = CombinedReason(reasons + other.reasons) + + override fun toString(): String = "[${reasons.joinToString("; ")}]" + + override fun hashCode(): Int = exprs.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Reason) return false + if (other.javaClass != javaClass) return false + return exprs == other.exprs + } +} + +class CombinedReason(override val reasons: List) : Reason() + +object PoReason : Reason() { + + override val reasons = emptyList() + override val expressions: List> = listOf() +} + +class RelationReason(val relation: Relation) : Reason() { + + override val expressions: List> = listOf(relation.declRef) + + override fun toString(): String = "REL(${relation.decl.name})" +} + +sealed class DerivedReason( + val rf: Relation, + val w: E, + private val wRfRelation: Reason, + private val name: String, +) : Reason() { + + override val expressions: List> = + listOfNotNull(rf.declRef, w.guardExpr, rf.from.interferenceCond(w)) + wRfRelation.exprs + + override fun toString(): String = "$name(${rf.decl.name}, ${w.const.name}, $wRfRelation)" + + override fun hashCode(): Int = + 31 * (31 * (31 + w.hashCode()) + rf.hashCode()) + wRfRelation.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DerivedReason<*>) return false + if (other.javaClass != javaClass) return false + return w == other.w && rf == other.rf && wRfRelation == other.wRfRelation + } +} + +class WriteSerializationReason(rf: Relation, w: E, val wBeforeRf: Reason) : + DerivedReason(rf, w, wBeforeRf, "WS") + +class FromReadReason(rf: Relation, w: E, val wAfterRf: Reason) : + DerivedReason(rf, w, wAfterRf, "FR") diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/PreventivePropagatorOcChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/PreventivePropagatorOcChecker.kt deleted file mode 100644 index b420177ac1..0000000000 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/PreventivePropagatorOcChecker.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2024 Budapest University of Technology and Economics - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package hu.bme.mit.theta.analysis.algorithm.oc - -import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.core.type.Expr -import hu.bme.mit.theta.core.type.booltype.BoolExprs.Not -import hu.bme.mit.theta.core.type.booltype.BoolExprs.True -import hu.bme.mit.theta.core.type.booltype.BoolType -import hu.bme.mit.theta.solver.SolverStatus - -class PreventivePropagatorOcChecker : UserPropagatorOcChecker() { - - private lateinit var reads: Map, List> - private val preventedClauses: MutableMap>> = mutableMapOf() - - private val Expr.prevented: Boolean get() = preventedClauses.any { this in it.value } - - override fun check( - events: Map, Map>>, - pos: List>, - rfs: Map, Set>>, - ): SolverStatus? { - reads = events.keys.associateWith { v -> events[v]!!.values.flatten().filter { it.type == EventType.READ } } - return super.check(events, pos, rfs) - } - - override fun propagate(expr: Expr) { - super.propagate(expr) - preventivePropagation() - } - - override fun pop(levels: Int) { - super.pop(levels) - preventedClauses.keys.filter { it > solverLevel }.forEach { preventedClauses.remove(it) } - } - - private fun preventivePropagation() { - val rels = partialAssignment.peek().rels - writes.forEach { (v, ws) -> - val rs = reads[v] ?: emptyList() - for (w in ws) for (r in rs) { - rels[r.clkId][w.clkId]?.let { reason -> // r -> w - rfs[v]?.find { it.from == w && it.to == r }?.let { rf -> // rf{w->r} - if (!rf.declRef.prevented) { - propagateUnit(reason, Not(rf.declRef)) - preventedClauses.getOrPut(solverLevel) { mutableSetOf() }.add(rf.declRef) - } - } - } - rels[w.clkId][r.clkId]?.let { wrReason -> - for (w0 in ws) - rels[w0.clkId][w.clkId]?.let { w0wReason -> // w0 -> w -> r - rfs[v]?.find { it.from == w0 && it.to == r }?.let { rf -> // rf{w0->r} - val reason = wrReason and w0wReason - if (partialAssignment.any { it.relation == rf } && !w.guardExpr.prevented) { - propagateUnit(reason, Not(w.guardExpr)) - preventedClauses.getOrPut(solverLevel) { mutableSetOf() }.add(w.guardExpr) - } - if (partialAssignment.any { it.event == w } && !rf.declRef.prevented) { - propagateUnit(reason, Not(rf.declRef)) - preventedClauses.getOrPut(solverLevel) { mutableSetOf() }.add(rf.declRef) - } - } - } - } - } - } - } - - private fun propagateUnit(reason: Reason, consequence: Expr) = - userPropagator.propagateConsequence(reason.exprs.filter { it != True() }, consequence) -} \ No newline at end of file diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/UserPropagatorOcChecker.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/UserPropagatorOcChecker.kt index 06a25a6ae5..5df28111e5 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/UserPropagatorOcChecker.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/UserPropagatorOcChecker.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.algorithm.oc import hu.bme.mit.theta.core.decl.VarDecl @@ -23,103 +22,226 @@ import hu.bme.mit.theta.solver.Solver import hu.bme.mit.theta.solver.SolverStatus import hu.bme.mit.theta.solver.javasmt.JavaSMTSolverFactory import hu.bme.mit.theta.solver.javasmt.JavaSMTUserPropagator -import org.sosy_lab.java_smt.SolverContextFactory.Solvers.Z3 import java.util.* +import org.sosy_lab.java_smt.SolverContextFactory.Solvers.Z3 -open class UserPropagatorOcChecker : OcCheckerBase() { - - protected val partialAssignment = Stack>() - protected lateinit var writes: Map, List> - protected lateinit var flatWrites: List - protected lateinit var rfs: Map, Set>> - private lateinit var flatRfs: List> - - protected val userPropagator: JavaSMTUserPropagator = object : JavaSMTUserPropagator() { - override fun onKnownValue(expr: Expr, value: Boolean) { - if (value) propagate(expr) - } - - override fun onFinalCheck() = - flatWrites.filter { w -> w.guard.isEmpty() || partialAssignment.any { it.event == w } }.forEach { w -> - propagate(w) - } - - override fun onPush() { - solverLevel++ - } - - override fun onPop(levels: Int) = pop(levels) - } - - override val solver: Solver = JavaSMTSolverFactory.create(Z3, arrayOf()).createSolverWithPropagators(userPropagator) - protected var solverLevel: Int = 0 - - override fun check( - events: Map, Map>>, - pos: List>, - rfs: Map, Set>>, - ): SolverStatus? { - writes = events.keys.associateWith { v -> events[v]!!.values.flatten().filter { it.type == EventType.WRITE } } - flatWrites = this.writes.values.flatten() - this.rfs = rfs - flatRfs = rfs.values.flatten() - - val clkSize = events.values.flatMap { it.values.flatten() }.maxOf { it.clkId } + 1 - val initialRels = Array(clkSize) { Array(clkSize) { null } } - pos.forEach { setAndClose(initialRels, it) } - partialAssignment.push(OcAssignment(rels = initialRels)) - - flatRfs.forEach { rf -> userPropagator.registerExpression(rf.declRef) } - flatWrites.forEach { w -> if (w.guard.isNotEmpty()) userPropagator.registerExpression(w.guardExpr) } - - return solver.check() +class UserPropagatorOcChecker : OcCheckerBase() { + + private lateinit var writes: Map, List> + private lateinit var rfs: Map, Set>> + private lateinit var wss: Map, Set>> + private lateinit var flatWrites: List + private lateinit var flatRfs: List> + private lateinit var flatWss: List> + private lateinit var interferenceCondToEvents: Map, List>> + private val partialAssignment = Stack>() + + private val userPropagator: JavaSMTUserPropagator = + object : JavaSMTUserPropagator() { + override fun onKnownValue(expr: Expr, value: Boolean) { + if (value) propagate(expr) + } + + override fun onFinalCheck() = + flatWrites + .filter { w -> w.guard.isEmpty() || partialAssignment.any { it.event == w } } + .forEach { w -> propagate(w) } + + override fun onPush() { + solverLevel++ + } + + override fun onPop(levels: Int) = pop(levels) } - override fun getRelations(): Array>? = partialAssignment.lastOrNull()?.rels?.copy() - - protected open fun propagate(expr: Expr) { - flatRfs.find { it.declRef == expr }?.let { rf -> propagate(rf) } - ?: flatWrites.filter { it.guardExpr == expr }.forEach { w -> propagate(w) } + private class PropagatorOcAssignment + private constructor( + stack: Stack>, + val solverLevel: Int, + rels: Array> = stack.peek().rels.copy(), + relation: Relation? = null, + event: E? = null, + val interference: Pair? = null, + ) : OcAssignment(rels, relation, event) { + + init { + stack.push(this) } - private fun propagate(rf: Relation) { - check(rf.type == RelationType.RF) - val assignment = OcAssignment(partialAssignment.peek().rels, rf, solverLevel) - partialAssignment.push(assignment) - val reason0 = setAndClose(assignment.rels, rf) - propagate(reason0) - - val writes = writes[rf.from.const.varDecl]!! - .filter { w -> w.guard.isEmpty() || partialAssignment.any { it.event == w } } - for (w in writes) { - val reason = derive(assignment.rels, rf, w) - propagate(reason) - } + constructor( + stack: Stack>, + rels: Array>, + ) : this(stack, 0, rels) + + constructor( + stack: Stack>, + e: E, + solverLevel: Int, + ) : this(stack, solverLevel, event = e) + + constructor( + stack: Stack>, + r: Relation, + solverLevel: Int, + ) : this(stack, solverLevel, relation = r) + + constructor( + stack: Stack>, + i: Pair, + solverLevel: Int, + ) : this(stack, solverLevel, interference = i) + } + + override val solver: Solver = + JavaSMTSolverFactory.create(Z3, arrayOf()).createSolverWithPropagators(userPropagator) + private var solverLevel: Int = 0 + + override fun check( + events: Map, Map>>, + pos: List>, + rfs: Map, Set>>, + wss: Map, Set>>, + ): SolverStatus? { + writes = + events.keys.associateWith { v -> + events[v]!!.values.flatten().filter { it.type == EventType.WRITE } + } + flatWrites = this.writes.values.flatten() + this.rfs = rfs + flatRfs = rfs.values.flatten() + this.wss = wss + flatWss = wss.values.flatten() + + val clkSize = events.values.flatMap { it.values.flatten() }.maxOf { it.clkId } + 1 + val initialRels = Array(clkSize) { Array(clkSize) { null } } + pos.forEach { setAndClose(initialRels, it) } + PropagatorOcAssignment(partialAssignment, initialRels) + registerExpressions() + + val result = solver.check() + if (result.isUnsat) return result + return finalWsCheck() ?: return result + } + + override fun getRelations(): Array>? = partialAssignment.lastOrNull()?.rels?.copy() + + private fun registerExpressions() { + flatRfs.forEach { rf -> userPropagator.registerExpression(rf.declRef) } + flatWrites.forEach { w -> + if (w.guard.isNotEmpty()) userPropagator.registerExpression(w.guardExpr) } - private fun propagate(w: E) { - check(w.type == EventType.WRITE) - rfs[w.const.varDecl]?.filter { r -> partialAssignment.any { it.relation == r } }?.let { rfs -> - val assignment = OcAssignment(partialAssignment.peek().rels, w, solverLevel) - partialAssignment.push(assignment) - for (rf in rfs) { - val reason = derive(assignment.rels, rf, w) - propagate(reason) - } + val interferenceToEvents = mutableMapOf, MutableList>>() + flatWrites.forEach { w1 -> + flatWrites.forEach { w2 -> + if (w1 != w2) { + w1.interferenceCond(w2)?.let { + userPropagator.registerExpression(it) + interferenceToEvents.getOrPut(it) { mutableListOf() }.add(w1 to w2) + } } + } } + interferenceCondToEvents = interferenceToEvents + } + + private fun interferenceKnown(w1: E, w2: E) = + w1.interferenceCond(w2) == null || partialAssignment.any { it.interference == w1 to w2 } + + private fun propagate(expr: Expr) { + flatRfs + .find { it.declRef == expr } + ?.let { rf -> + propagate(rf) + return + } + flatWss + .find { it.declRef == expr } + ?.let { ws -> + propagate(ws) + return + } + flatWrites.filter { it.guardExpr == expr }.forEach { w -> propagate(w) } + interferenceCondToEvents[expr]?.forEach { (w1, w2) -> propagate(w1, w2) } + } + + private fun propagate(rel: Relation) { + val assignment = PropagatorOcAssignment(partialAssignment, rel, solverLevel) + val reason0 = setAndClose(assignment.rels, rel) + propagate(reason0) + + when (rel.type) { + RelationType.RF -> { + writes[rel.from.const.varDecl]!! + .filter { w -> + (w.guard.isEmpty() || partialAssignment.any { it.event == w }) && + interferenceKnown(rel.from, w) + } + .forEach { w -> + val reason = derive(assignment.rels, rel, w) + propagate(reason) + } + } + + RelationType.WS -> { + rfs[rel.from.const.varDecl] + ?.filter { rf -> rf.from == rel.from && partialAssignment.any { it.relation == rf } } + ?.forEach { rf -> + val reason = derive(assignment.rels, rf, rel.to) + propagate(reason) + } + } - override fun propagate(reason: Reason?): Boolean { - reason ?: return false - propagated.add(reason) - userPropagator.propagateConflict(reason.exprs) - return true + else -> {} } - - protected open fun pop(levels: Int) { - solverLevel -= levels - while (partialAssignment.isNotEmpty() && partialAssignment.peek().solverLevel > solverLevel) { - partialAssignment.pop() - } + } + + private fun propagate(w: E) { + check(w.type == EventType.WRITE) + val assignment = PropagatorOcAssignment(partialAssignment, w, solverLevel) + + rfs[w.const.varDecl] + ?.filter { rf -> + partialAssignment.any { it.relation == rf } && interferenceKnown(rf.from, w) + } + ?.forEach { rf -> + val reason = derive(assignment.rels, rf, w) + propagate(reason) + } + } + + private fun propagate(w1: E, w2: E) { + check(w1.type == EventType.WRITE && w2.type == EventType.WRITE) + val assignment = PropagatorOcAssignment(partialAssignment, w1 to w2, solverLevel) + if (partialAssignment.none { it.event == w1 } || partialAssignment.none { it.event == w2 }) + return + + rfs[w1.const.varDecl] + ?.filter { rf -> rf.from == w1 && partialAssignment.any { it.relation == rf } } + ?.forEach { rf -> + val reason = derive(assignment.rels, rf, w2) + propagate(reason) + } + } + + override fun propagate(reason: Reason?): Boolean { + reason ?: return false + propagated.add(reason) + userPropagator.propagateConflict(reason.exprs) + return true + } + + private fun pop(levels: Int) { + solverLevel -= levels + while (partialAssignment.isNotEmpty() && partialAssignment.peek().solverLevel > solverLevel) { + partialAssignment.pop() } -} \ No newline at end of file + } + + private fun finalWsCheck(): SolverStatus? { + val rels = partialAssignment.peek().rels + val unassignedWss = finalWsCheck(rels, wss) + unassignedWss.forEach { ws -> userPropagator.registerExpression(ws.declRef) } + return solver.check() + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/ItpRefToPtrPrec.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/ItpRefToPtrPrec.kt index c3ec804041..b75fa112c3 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/ItpRefToPtrPrec.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/ItpRefToPtrPrec.kt @@ -21,27 +21,27 @@ import hu.bme.mit.theta.analysis.expr.refinement.ItpRefutation import hu.bme.mit.theta.analysis.expr.refinement.RefutationToPrec import hu.bme.mit.theta.common.Utils -/** - * Transformer from interpolant refutation to pointer precision. - */ +/** Transformer from interpolant refutation to pointer precision. */ class ItpRefToPtrPrec

(private val innerRefToPrec: RefutationToPrec) : - RefutationToPrec, ItpRefutation> { + RefutationToPrec, ItpRefutation> { - override fun toPrec(refutation: ItpRefutation, index: Int): PtrPrec

{ - val newDerefs = refutation[index].dereferences - val innerPrec = innerRefToPrec.toPrec(refutation, index).repatch() - return PtrPrec(innerPrec, newDerefs.flatMap { it.ops }.toSet(), - if (newDerefs.isEmpty()) 0 else refutation.size() - index) - } + override fun toPrec(refutation: ItpRefutation, index: Int): PtrPrec

{ + val newDerefs = refutation[index].dereferences + val innerPrec = innerRefToPrec.toPrec(refutation, index).repatch() + return PtrPrec( + innerPrec, + newDerefs.flatMap { it.ops }.toSet(), + if (newDerefs.isEmpty()) 0 else refutation.size() - index, + ) + } - override fun join(prec1: PtrPrec

, prec2: PtrPrec

): PtrPrec

{ - Preconditions.checkNotNull(prec1) - Preconditions.checkNotNull(prec2) - return PtrPrec(innerRefToPrec.join(prec1.innerPrec, prec2.innerPrec)) - } + override fun join(prec1: PtrPrec

, prec2: PtrPrec

): PtrPrec

{ + Preconditions.checkNotNull(prec1) + Preconditions.checkNotNull(prec2) + return PtrPrec(innerRefToPrec.join(prec1.innerPrec, prec2.innerPrec)) + } - override fun toString(): String { - return Utils.lispStringBuilder(javaClass.simpleName).aligned().add(innerRefToPrec) - .toString() - } + override fun toString(): String { + return Utils.lispStringBuilder(javaClass.simpleName).aligned().add(innerRefToPrec).toString() + } } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAction.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAction.kt index 015084a889..f5a4875219 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAction.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAction.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.ptr import com.google.common.base.Preconditions @@ -32,57 +31,70 @@ import hu.bme.mit.theta.core.utils.ExprUtils private val varList = LinkedHashMap, LinkedHashMap>>() -abstract class PtrAction(writeTriples: WriteTriples = emptyMap(), val inCnt: Int = 0) : StmtAction() { +abstract class PtrAction(writeTriples: WriteTriples = emptyMap(), val inCnt: Int = 0) : + StmtAction() { - abstract val stmtList: List + abstract val stmtList: List - private val expanded by lazy { createStmtList(writeTriples) } + private val expanded by lazy { createStmtList(writeTriples) } - var cnts = LinkedHashMap() - fun nextWriteTriples(): WriteTriples = expanded.first + var cnts = LinkedHashMap() - final override fun getStmts(): List = expanded.second + fun nextWriteTriples(): WriteTriples = expanded.first - fun havocStmts(): List = expanded.third + final override fun getStmts(): List = expanded.second - private fun createStmtList(writeTriples: WriteTriples): Triple, List> { - val nextWriteTriples = writeTriples.toMutable() - val havocStmtList = ArrayList() - val stmtList = ArrayList() - val vargen = { it: String, type: Type -> - val current = cnts.getOrDefault(it, inCnt) - cnts[it] = current + 1 - val iMap = varList.getOrPut(Pair(it, type)) { LinkedHashMap() } - iMap.getOrPut(current) { Var("__${it}_$current", type) } - } - val lookup = LinkedHashMap, Pair, Expr<*>>>() - for (stmt in this.stmtList.map { it.uniqueDereferences(vargen, lookup) }) { - val preList = ArrayList() - val postList = ArrayList() + fun havocStmts(): List = expanded.third - for ((deref, type) in stmt.dereferencesWithAccessTypes) { - Preconditions.checkState(deref.uniquenessIdx.isPresent, - "Incomplete dereferences (missing uniquenessIdx) are not handled properly.") - val expr = deref.getIte(nextWriteTriples) - if (type == AccessType.WRITE) { - val writeExpr = ExprUtils.simplify(IntExprs.Add(expr, IntExprs.Int(1))) - nextWriteTriples.getOrPut(deref.type) { ArrayList() } - .add(Triple(lookup[deref]!!.first, lookup[deref]!!.second, deref.uniquenessIdx.get())) - postList.add(Stmts.Assume(ExprUtils.simplify(BoolExprs.And(listOf( - AbstractExprs.Eq(writeExpr, deref.uniquenessIdx.get()), - ))))) - } else { - preList.add( - Stmts.Assume(ExprUtils.simplify(AbstractExprs.Eq(expr, deref.uniquenessIdx.get())))) - } -// postList.add(Stmts.Assume(Eq(vargen("value", deref.type).ref, deref))) // debug mode - } + private fun createStmtList( + writeTriples: WriteTriples + ): Triple, List> { + val nextWriteTriples = writeTriples.toMutable() + val havocStmtList = ArrayList() + val stmtList = ArrayList() + val vargen = { it: String, type: Type -> + val current = cnts.getOrDefault(it, inCnt) + cnts[it] = current + 1 + val iMap = varList.getOrPut(Pair(it, type)) { LinkedHashMap() } + iMap.getOrPut(current) { Var("__${it}_$current", type) } + } + val lookup = LinkedHashMap, Pair, Expr<*>>>() + for (stmt in this.stmtList.map { it.uniqueDereferences(vargen, lookup) }) { + val preList = ArrayList() + val postList = ArrayList() - stmtList.addAll(preList) - stmtList.add(stmt) - havocStmtList.add(stmt) - stmtList.addAll(postList) + for ((deref, type) in stmt.dereferencesWithAccessTypes) { + Preconditions.checkState( + deref.uniquenessIdx.isPresent, + "Incomplete dereferences (missing uniquenessIdx) are not handled properly.", + ) + val expr = deref.getIte(nextWriteTriples) + if (type == AccessType.WRITE) { + val writeExpr = ExprUtils.simplify(IntExprs.Add(expr, IntExprs.Int(1))) + nextWriteTriples + .getOrPut(deref.type) { ArrayList() } + .add(Triple(lookup[deref]!!.first, lookup[deref]!!.second, deref.uniquenessIdx.get())) + postList.add( + Stmts.Assume( + ExprUtils.simplify( + BoolExprs.And(listOf(AbstractExprs.Eq(writeExpr, deref.uniquenessIdx.get()))) + ) + ) + ) + } else { + preList.add( + Stmts.Assume(ExprUtils.simplify(AbstractExprs.Eq(expr, deref.uniquenessIdx.get()))) + ) } - return Triple(nextWriteTriples, stmtList, havocStmtList) + // postList.add(Stmts.Assume(Eq(vargen("value", deref.type).ref, deref))) // + // debug mode + } + + stmtList.addAll(preList) + stmtList.add(stmt) + havocStmtList.add(stmt) + stmtList.addAll(postList) } -} \ No newline at end of file + return Triple(nextWriteTriples, stmtList, havocStmtList) + } +} diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysis.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysis.kt index 4eb29ec625..a7c9983c7b 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysis.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysis.kt @@ -24,49 +24,57 @@ import hu.bme.mit.theta.core.stmt.Stmt /** * Pointer analysis in CEGAR * - * PtrState has some inner state (presumably PredState or ExplState), and a list of tuples (per SMT type) that store - * last-known values of specific memory places. + * PtrState has some inner state (presumably PredState or ExplState), and a list of tuples (per SMT + * type) that store last-known values of specific memory places. * - * PtrTransFunc takes a list of statements, possibly including Dereferences in both left- and right-hand sides in them, - * and using the last-known values (with havoc as fallback) constructs safeguards that constraint how the expression may - * read from writes. New values are placed in the resulting state. Values are never erased. - * - * PtrAction is a special StmtAction, which needs some pre-processing based on the current known memory values. + * PtrTransFunc takes a list of statements, possibly including Dereferences in both left- and + * right-hand sides in them, and using the last-known values (with havoc as fallback) constructs + * safeguards that constraint how the expression may read from writes. New values are placed in the + * resulting state. Values are never erased. * + * PtrAction is a special StmtAction, which needs some pre-processing based on the current known + * memory values. */ +class PtrAnalysis( + private val innerAnalysis: Analysis, + private val isHavoc: Boolean = false, +) : Analysis, PtrAction, PtrPrec

> { -class PtrAnalysis(private val innerAnalysis: Analysis, - private val isHavoc: Boolean = false) : - Analysis, PtrAction, PtrPrec

> { - - override fun getPartialOrd(): PartialOrd> = innerAnalysis.partialOrd.getPtrPartialOrd() + override fun getPartialOrd(): PartialOrd> = + innerAnalysis.partialOrd.getPtrPartialOrd() - override fun getInitFunc(): InitFunc, PtrPrec

> = innerAnalysis.initFunc.getPtrInitFunc() + override fun getInitFunc(): InitFunc, PtrPrec

> = + innerAnalysis.initFunc.getPtrInitFunc() - override fun getTransFunc(): TransFunc, PtrAction, PtrPrec

> = innerAnalysis.transFunc.getPtrTransFunc( - isHavoc) + override fun getTransFunc(): TransFunc, PtrAction, PtrPrec

> = + innerAnalysis.transFunc.getPtrTransFunc(isHavoc) } -fun PartialOrd.getPtrPartialOrd(): PartialOrd> = PartialOrd { state1, state2 -> +fun PartialOrd.getPtrPartialOrd(): PartialOrd> = + PartialOrd { state1, state2 -> isLeq(state1.innerState, state2.innerState) -} + } -fun InitFunc.getPtrInitFunc(): InitFunc, PtrPrec

> = InitFunc { prec -> +fun InitFunc.getPtrInitFunc(): InitFunc, PtrPrec

> = + InitFunc { prec -> getInitStates(prec.innerPrec.patch(emptyMap())).map { PtrState(it) } -} + } fun TransFunc.getPtrTransFunc( - isHavoc: Boolean = false): TransFunc, PtrAction, PtrPrec

> = TransFunc { state, action, prec -> - val writeTriples = action.nextWriteTriples() - val patchedPrec = prec.innerPrec.patch(writeTriples) - val exprAction: ExprAction = if (isHavoc) { - object : StmtAction() { - override fun getStmts(): List = action.havocStmts() // stmts without pre- and postconditions - } + isHavoc: Boolean = false +): TransFunc, PtrAction, PtrPrec

> = TransFunc { state, action, prec -> + val writeTriples = action.nextWriteTriples() + val patchedPrec = prec.innerPrec.patch(writeTriples) + val exprAction: ExprAction = + if (isHavoc) { + object : StmtAction() { + override fun getStmts(): List = + action.havocStmts() // stmts without pre- and postconditions + } } else { - action - } - getSuccStates(state.innerState, exprAction, patchedPrec).map { - PtrState(it.repatch(), action.cnts.values.maxOrNull() ?: action.inCnt) + action } + getSuccStates(state.innerState, exprAction, patchedPrec).map { + PtrState(it.repatch(), action.cnts.values.maxOrNull() ?: action.inCnt) + } } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrUtils.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrUtils.kt index 3cc08a0cf1..8656387c39 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrUtils.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrUtils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.ptr import com.google.common.base.Preconditions @@ -39,173 +38,211 @@ import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.core.utils.TypeUtils - typealias WriteTriples = Map, Expr<*>, Expr>>> -typealias MutableWriteTriples = MutableMap, Expr<*>, Expr>>> + +typealias MutableWriteTriples = + MutableMap, Expr<*>, Expr>>> fun WriteTriples.toMutable(): MutableWriteTriples = - LinkedHashMap(this.map { Pair(it.key, ArrayList(it.value)) }.toMap()) + LinkedHashMap(this.map { Pair(it.key, ArrayList(it.value)) }.toMap()) enum class AccessType { - READ, WRITE + READ, + WRITE, } val Stmt.dereferencesWithAccessTypes: List, AccessType>> - get() = when (this) { - is MemoryAssignStmt<*, *, *> -> expr.dereferences.map { Pair(it, AccessType.READ) } + listOf( - Pair(deref, AccessType.WRITE)) + deref.ops.flatMap { it.dereferences }.map { Pair(it, AccessType.READ) } - - is AssignStmt<*> -> expr.dereferences.map { Pair(it, AccessType.READ) } - is AssumeStmt -> cond.dereferences.map { Pair(it, AccessType.READ) } - is SequenceStmt -> stmts.flatMap { it.dereferencesWithAccessTypes } - is HavocStmt<*> -> listOf() - is SkipStmt -> listOf() - is NonDetStmt -> error("NonDetStmts do not have a clearly defined sequence") - is LoopStmt -> error("LoopStmt do not have a clearly defined sequence") - is IfStmt -> error("IfStmt do not have a clearly defined sequence") - else -> TODO("Not yet implemented for ${this.javaClass.simpleName}") + get() = + when (this) { + is MemoryAssignStmt<*, *, *> -> + expr.dereferences.map { Pair(it, AccessType.READ) } + + listOf(Pair(deref, AccessType.WRITE)) + + deref.ops.flatMap { it.dereferences }.map { Pair(it, AccessType.READ) } + + is AssignStmt<*> -> expr.dereferences.map { Pair(it, AccessType.READ) } + is AssumeStmt -> cond.dereferences.map { Pair(it, AccessType.READ) } + is SequenceStmt -> stmts.flatMap { it.dereferencesWithAccessTypes } + is HavocStmt<*> -> listOf() + is SkipStmt -> listOf() + is NonDetStmt -> error("NonDetStmts do not have a clearly defined sequence") + is LoopStmt -> error("LoopStmt do not have a clearly defined sequence") + is IfStmt -> error("IfStmt do not have a clearly defined sequence") + else -> TODO("Not yet implemented for ${this.javaClass.simpleName}") } val Expr<*>.dereferences: List> - get() = if (this is Dereference<*, *, *>) { - listOf(this) + ops.flatMap { it.dereferences } + get() = + if (this is Dereference<*, *, *>) { + listOf(this) + ops.flatMap { it.dereferences } } else { - ops.flatMap { it.dereferences } + ops.flatMap { it.dereferences } } - fun SequenceStmt.collapse(): Stmt = - if (stmts.size == 1 && stmts[0] is SequenceStmt) { - (stmts[0] as SequenceStmt).collapse() - } else if (stmts.size == 1) { - stmts[0] - } else { - this + if (stmts.size == 1 && stmts[0] is SequenceStmt) { + (stmts[0] as SequenceStmt).collapse() + } else if (stmts.size == 1) { + stmts[0] + } else { + this + } + +fun Stmt.uniqueDereferences( + vargen: (String, Type) -> VarDecl<*>, + lookup: MutableMap, Pair, Expr<*>>>, +): Stmt { + val ret = ArrayList() + val newStmt = + when (this) { + is MemoryAssignStmt<*, *, *> -> { + MemoryAssignStmt.create( + deref.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second + as Dereference<*, *, *>, + expr.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second, + ) + } + + is AssignStmt<*> -> + AssignStmt.of( + TypeUtils.cast(varDecl, varDecl.type), + TypeUtils.cast( + expr.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second, + varDecl.type, + ), + ) + + is AssumeStmt -> + AssumeStmt.of(cond.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second) + is SequenceStmt -> Stmts.SequenceStmt(stmts.map { it.uniqueDereferences(vargen, lookup) }) + is HavocStmt<*> -> this + is SkipStmt -> this + is NonDetStmt -> error("NonDetStmts do not have a clearly defined sequence") + is LoopStmt -> error("LoopStmt do not have a clearly defined sequence") + is IfStmt -> error("IfStmt do not have a clearly defined sequence") + else -> TODO("Not yet implemented for ${this.javaClass.simpleName}") } - -fun Stmt.uniqueDereferences(vargen: (String, Type) -> VarDecl<*>, - lookup: MutableMap, Pair, Expr<*>>>): Stmt { - val ret = ArrayList() - val newStmt = when (this) { - is MemoryAssignStmt<*, *, *> -> { - MemoryAssignStmt.create( - deref.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second as Dereference<*, *, *>, - expr.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second) - } - - is AssignStmt<*> -> AssignStmt.of( - TypeUtils.cast(varDecl, varDecl.type), - TypeUtils.cast(expr.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second, varDecl.type)) - - is AssumeStmt -> AssumeStmt.of(cond.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second) - is SequenceStmt -> Stmts.SequenceStmt(stmts.map { it.uniqueDereferences(vargen, lookup) }) - is HavocStmt<*> -> this - is SkipStmt -> this - is NonDetStmt -> error("NonDetStmts do not have a clearly defined sequence") - is LoopStmt -> error("LoopStmt do not have a clearly defined sequence") - is IfStmt -> error("IfStmt do not have a clearly defined sequence") - else -> TODO("Not yet implemented for ${this.javaClass.simpleName}") - } - return SequenceStmt.of(ret + newStmt).collapse() + return SequenceStmt.of(ret + newStmt).collapse() } -fun Expr.uniqueDereferences(vargen: (String, Type) -> VarDecl<*>, - lookup: MutableMap, Pair, Expr<*>>>): Pair, Expr> = - if (this is Dereference<*, *, T>) { - val ret = ArrayList() - Preconditions.checkState(this.uniquenessIdx.isEmpty, "Only non-pretransformed dereferences should be here") - val arrayExpr = ExprUtils.simplify( - array.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second) - val arrayLaterRef = if (arrayExpr !is LitExpr<*>) { - val arrayConst = vargen("a", array.type).ref - ret.add(Assume(Eq(arrayConst, arrayExpr))) - arrayConst - } else { - arrayExpr - } - val offsetExpr = ExprUtils.simplify( - offset.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second) - val offsetLaterRef = if (offsetExpr !is LitExpr<*>) { - val offsetConst = vargen("o", offset.type).ref - ret.add(Assume(Eq(offsetConst, offsetExpr))) - offsetConst - } else { - offsetExpr - } - val deref = withUniquenessExpr(vargen("idx", Int()).ref as Expr) - val retDeref = deref.map { - it.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second - } as Dereference<*, *, T> - lookup[retDeref] = Pair(arrayLaterRef, offsetLaterRef) - Pair(ret, retDeref) - } else { - val ret = ArrayList() - Pair(ret, - this.withOps(this.ops.map { it.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second })) - } +fun Expr.uniqueDereferences( + vargen: (String, Type) -> VarDecl<*>, + lookup: MutableMap, Pair, Expr<*>>>, +): Pair, Expr> = + if (this is Dereference<*, *, T>) { + val ret = ArrayList() + Preconditions.checkState( + this.uniquenessIdx.isEmpty, + "Only non-pretransformed dereferences should be here", + ) + val arrayExpr = + ExprUtils.simplify( + array.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second + ) + val arrayLaterRef = + if (arrayExpr !is LitExpr<*>) { + val arrayConst = vargen("a", array.type).ref + ret.add(Assume(Eq(arrayConst, arrayExpr))) + arrayConst + } else { + arrayExpr + } + val offsetExpr = + ExprUtils.simplify( + offset.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second + ) + val offsetLaterRef = + if (offsetExpr !is LitExpr<*>) { + val offsetConst = vargen("o", offset.type).ref + ret.add(Assume(Eq(offsetConst, offsetExpr))) + offsetConst + } else { + offsetExpr + } + val deref = withUniquenessExpr(vargen("idx", Int()).ref as Expr) + val retDeref = + deref.map { it.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second } + as Dereference<*, *, T> + lookup[retDeref] = Pair(arrayLaterRef, offsetLaterRef) + Pair(ret, retDeref) + } else { + val ret = ArrayList() + Pair( + ret, + this.withOps( + this.ops.map { it.uniqueDereferences(vargen, lookup).also { ret.addAll(it.first) }.second } + ), + ) + } object TopCollection : Set> { - override val size: Int - get() = error("No size information known for TopCollection") + override val size: Int + get() = error("No size information known for TopCollection") - override fun contains(element: Expr<*>): Boolean = true + override fun contains(element: Expr<*>): Boolean = true - override fun containsAll(elements: Collection>) = true + override fun containsAll(elements: Collection>) = true - override fun isEmpty(): Boolean = false + override fun isEmpty(): Boolean = false - override fun iterator(): Iterator> = error("TopCollection not iterable") + override fun iterator(): Iterator> = error("TopCollection not iterable") } enum class PtrTracking { - ALWAYS_TOP, // always keep track of all pointer accesses - ANY_MATCH, // if any of the arguments match, keep track - ALL_MATCH, // if all of the arguments match, keep track - NONE // do not keep track of any pointer acceses + ALWAYS_TOP, // always keep track of all pointer accesses + ANY_MATCH, // if any of the arguments match, keep track + ALL_MATCH, // if all of the arguments match, keep track + NONE, // do not keep track of any pointer acceses } -fun Dereference.getIte(writeTriples: WriteTriples): Expr { - val list = writeTriples[type] ?: emptyList() - return list.fold(Int(0) as Expr) { elze, (arr, off, value) -> - IteExpr.of(BoolExprs.And( - listOf(Eq(arr, array), Eq(off, offset))), value, elze) - } +fun Dereference.getIte( + writeTriples: WriteTriples +): Expr { + val list = writeTriples[type] ?: emptyList() + return list.fold(Int(0) as Expr) { elze, (arr, off, value) -> + IteExpr.of(BoolExprs.And(listOf(Eq(arr, array), Eq(off, offset))), value, elze) + } } -fun S.patch(writeTriples: WriteTriples): S = when (this) { +fun S.patch(writeTriples: WriteTriples): S = + when (this) { is PredState -> PredState.of(preds.map { it.patch(writeTriples) }) as S is ExplState -> this - is PtrState<*> -> (this as PtrState).copy(innerState = innerState.patch(writeTriples)) as S + is PtrState<*> -> + (this as PtrState).copy(innerState = innerState.patch(writeTriples)) as S else -> error("State $this is not supported") -} + } -fun

P.patch(writeTriples: WriteTriples): P = when (this) { +fun

P.patch(writeTriples: WriteTriples): P = + when (this) { is PredPrec -> PredPrec.of(preds.map { it.patch(writeTriples) }) as P is ExplPrec -> this else -> error("Prec $this is not supported") -} + } -private fun Expr.patch(writeTriples: WriteTriples): Expr = when (this) { - is Dereference<*, *, T> -> withUniquenessExpr(getIte(writeTriples)).map { it.patch(writeTriples) } +private fun Expr.patch(writeTriples: WriteTriples): Expr = + when (this) { + is Dereference<*, *, T> -> + withUniquenessExpr(getIte(writeTriples)).map { it.patch(writeTriples) } else -> map { it.patch(writeTriples) } -} - + } -fun

P.repatch(): P = when (this) { +fun

P.repatch(): P = + when (this) { is PredPrec -> PredPrec.of(preds.map { it.repatch() }) as P is ExplPrec -> this else -> error("Prec $this is not supported") -} + } - -fun S.repatch(): S = when (this) { +fun S.repatch(): S = + when (this) { is PredState -> PredState.of(preds.map(Expr::repatch)) as S is ExplState -> this else -> error("State $this is not supported") -} + } -private fun Expr.repatch(): Expr = when (this) { +private fun Expr.repatch(): Expr = + when (this) { is Dereference<*, *, T> -> withUniquenessExpr(Int(0)).map(Expr<*>::repatch) else -> map(Expr<*>::repatch) -} \ No newline at end of file + } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/CexMonitor.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/CexMonitor.kt index 32c78c9b0e..94875a8124 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/CexMonitor.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/CexMonitor.kt @@ -27,45 +27,43 @@ import hu.bme.mit.theta.common.logging.Logger * This monitor checks whether a new counterexample is found during the current iteration of CEGAR. * In most cases, finding the same counterexample again means that refinement is not progressing. * Warning: With some configurations (e.g. lazy pruning) it is NOT impossible that analysis will - * progress even if in some iterations a new cex is not found, but seems to be rare. - * However, if you think analysis should NOT be stopped by this monitor, disable it and check results. + * progress even if in some iterations a new cex is not found, but seems to be rare. However, if you + * think analysis should NOT be stopped by this monitor, disable it and check results. */ -class CexMonitor constructor( - private val logger: Logger, private val arg: ARG -) : Monitor { +class CexMonitor +constructor(private val logger: Logger, private val arg: ARG) : Monitor { - private val cexHashStorage = CexHashStorage() - var lastCex: ArgTrace? = null + private val cexHashStorage = CexHashStorage() + var lastCex: ArgTrace? = null - fun checkIfNewCexFound(): Boolean { - return if (arg.cexs.anyMatch { cex -> !cexHashStorage.contains(cex) }) { - logger.write(Logger.Level.VERBOSE, - "Counterexample hash check: new cex found successfully\n") - true - } else { - logger.write(Logger.Level.INFO, "Counterexample hash check: NO new cex found\n") - false - } + fun checkIfNewCexFound(): Boolean { + return if (arg.cexs.anyMatch { cex -> !cexHashStorage.contains(cex) }) { + logger.write(Logger.Level.VERBOSE, "Counterexample hash check: new cex found successfully\n") + true + } else { + logger.write(Logger.Level.INFO, "Counterexample hash check: NO new cex found\n") + false } + } - fun addNewCounterexample() { - val newCexs: List> = arg.cexs.filter { !cexHashStorage.contains(it) } - .toList() - assert(newCexs.size == 1, { "Found ${newCexs.size} new cex instead of one" }) + fun addNewCounterexample() { + val newCexs: List> = arg.cexs.filter { !cexHashStorage.contains(it) }.toList() + assert(newCexs.size == 1, { "Found ${newCexs.size} new cex instead of one" }) - lastCex = newCexs.get(0) - cexHashStorage.addData(lastCex) - } + lastCex = newCexs.get(0) + cexHashStorage.addData(lastCex) + } - private fun throwNotSolvable() { - throw NotSolvableException() - } + private fun throwNotSolvable() { + throw NotSolvableException() + } - override fun execute(checkpointName: String) { - when (checkpointName) { - "CegarChecker.unsafeARG" -> if (checkIfNewCexFound()) addNewCounterexample() else throwNotSolvable() - else -> throw RuntimeException( - "Unknown checkpoint name in CexMonitor execution: $checkpointName") - } + override fun execute(checkpointName: String) { + when (checkpointName) { + "CegarChecker.unsafeARG" -> + if (checkIfNewCexFound()) addNewCounterexample() else throwNotSolvable() + else -> + throw RuntimeException("Unknown checkpoint name in CexMonitor execution: $checkpointName") } + } } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/MonitorCheckpoint.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/MonitorCheckpoint.kt index 0e1faccd73..bf4b89832d 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/MonitorCheckpoint.kt +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/runtimemonitor/MonitorCheckpoint.kt @@ -16,57 +16,57 @@ package hu.bme.mit.theta.analysis.runtimemonitor /** - * This class handles the creation, registering and execution of monitor checkpoints. - * If you would like to add a new checkpoint do the following: - * 1, Add its name below to checkpointNames in the companion object - * (from then on it will be automatically created) - * 2, Wherever you would like to execute it, add a MonitorCheckpoint.execute() call - * 3, Register your monitors to it so they are executed when the checkpoint is executed: (MonitorCheckpoint.register) + * This class handles the creation, registering and execution of monitor checkpoints. If you would + * like to add a new checkpoint do the following: 1, Add its name below to checkpointNames in the + * companion object (from then on it will be automatically created) 2, Wherever you would like to + * execute it, add a MonitorCheckpoint.execute() call 3, Register your monitors to it so they + * are executed when the checkpoint is executed: (MonitorCheckpoint.register) */ class MonitorCheckpoint internal constructor(private val name: String) { - private val registeredMonitors: HashSet = HashSet() + private val registeredMonitors: HashSet = HashSet() - fun registerMonitor(m: Monitor) { - registeredMonitors.add(m) - } - - fun executeCheckpoint() { - registeredMonitors.forEach { monitor: Monitor -> monitor.execute(name) } - } + fun registerMonitor(m: Monitor) { + registeredMonitors.add(m) + } - companion object Checkpoints { + fun executeCheckpoint() { + registeredMonitors.forEach { monitor: Monitor -> monitor.execute(name) } + } - // Add any new checkpoints here - private val checkpointNames = setOf( - "CegarChecker.unsafeARG", - ) + companion object Checkpoints { - private val registeredCheckpoints: HashMap = HashMap() + // Add any new checkpoints here + private val checkpointNames = setOf("CegarChecker.unsafeARG") - init { - checkpointNames.forEach { registeredCheckpoints.put(it, MonitorCheckpoint(it)) } - } + private val registeredCheckpoints: HashMap = HashMap() - fun register(m: Monitor, checkpointName: String) { - assert(registeredCheckpoints.contains(checkpointName)) - { "Checkpoint name $checkpointName was not registered (add it in MonitorCheckpoint.kt)" } // see checkpointNames above - registeredCheckpoints[checkpointName]?.registerMonitor(m) ?: error( - "Checkpoint with name $checkpointName not found.") - } + init { + checkpointNames.forEach { registeredCheckpoints.put(it, MonitorCheckpoint(it)) } + } - fun execute(name: String) { - assert(registeredCheckpoints.contains(name)) - { "Checkpoint name $name was not registered (add it in MonitorCheckpoint.kt)" } // see checkpointNames above - registeredCheckpoints[name]?.executeCheckpoint() ?: error("Checkpoint with name $name not found.") - } + fun register(m: Monitor, checkpointName: String) { + assert(registeredCheckpoints.contains(checkpointName)) { + "Checkpoint name $checkpointName was not registered (add it in MonitorCheckpoint.kt)" + } // see checkpointNames above + registeredCheckpoints[checkpointName]?.registerMonitor(m) + ?: error("Checkpoint with name $checkpointName not found.") + } - fun reset() { - registeredCheckpoints.values.forEach { it.reset() } - } + fun execute(name: String) { + assert(registeredCheckpoints.contains(name)) { + "Checkpoint name $name was not registered (add it in MonitorCheckpoint.kt)" + } // see checkpointNames above + registeredCheckpoints[name]?.executeCheckpoint() + ?: error("Checkpoint with name $name not found.") } - private fun reset() { - registeredMonitors.clear() + fun reset() { + registeredCheckpoints.values.forEach { it.reset() } } -} \ No newline at end of file + } + + private fun reset() { + registeredMonitors.clear() + } +} diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiInitFunc.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiInitFunc.kt index 4d7e740082..24aac111a1 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiInitFunc.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiInitFunc.kt @@ -19,31 +19,44 @@ import hu.bme.mit.theta.analysis.InitFunc import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.State -class MultiInitFunc> -( - private val createInitialState: (LControl, RControl, DataState) -> MState, - private val dataInitFunc: InitFunc, - private val extractLeftControlPrec: (LPrec) -> LControlPrec, - private val leftControlInitFunc: InitFunc, - private val extractRightControlPrec: (RPrec) -> RControlPrec, - private val rightControlInitFunc: InitFunc, +class MultiInitFunc< + DataState : State, + LControl : State, + RControl : State, + LPrec : Prec, + RPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, + DataPrec : Prec, + MState : MultiState, +>( + private val createInitialState: (LControl, RControl, DataState) -> MState, + private val dataInitFunc: InitFunc, + private val extractLeftControlPrec: (LPrec) -> LControlPrec, + private val leftControlInitFunc: InitFunc, + private val extractRightControlPrec: (RPrec) -> RControlPrec, + private val rightControlInitFunc: InitFunc, ) : InitFunc> { - override fun getInitStates(prec: MultiPrec?): MutableCollection { - prec!! - val leftInitPrec: LControlPrec = extractLeftControlPrec(prec.leftPrec) - val rightInitPrec: RControlPrec = extractRightControlPrec(prec.rightPrec) - val leftInitStates: Collection = HashSet(leftControlInitFunc.getInitStates(leftInitPrec)) - val rightInitStates: Collection = HashSet(rightControlInitFunc.getInitStates(rightInitPrec)) - val dataInitStates: Collection = dataInitFunc.getInitStates(prec.dataPrec) - return leftInitStates.flatMap { leftInitState -> - rightInitStates.flatMap { rightInitState -> - dataInitStates.map { dataInitState -> - createInitialState(leftInitState, rightInitState, dataInitState) - } - } - }.toMutableSet() - } -} \ No newline at end of file + override fun getInitStates( + prec: MultiPrec? + ): MutableCollection { + prec!! + val leftInitPrec: LControlPrec = extractLeftControlPrec(prec.leftPrec) + val rightInitPrec: RControlPrec = extractRightControlPrec(prec.rightPrec) + val leftInitStates: Collection = + HashSet(leftControlInitFunc.getInitStates(leftInitPrec)) + val rightInitStates: Collection = + HashSet(rightControlInitFunc.getInitStates(rightInitPrec)) + val dataInitStates: Collection = dataInitFunc.getInitStates(prec.dataPrec) + return leftInitStates + .flatMap { leftInitState -> + rightInitStates.flatMap { rightInitState -> + dataInitStates.map { dataInitState -> + createInitialState(leftInitState, rightInitState, dataInitState) + } + } + } + .toMutableSet() + } +} diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiPartialOrd.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiPartialOrd.kt index 68b3c77b27..56d469471d 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiPartialOrd.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiPartialOrd.kt @@ -18,24 +18,30 @@ package hu.bme.mit.theta.analysis.multi import hu.bme.mit.theta.analysis.PartialOrd import hu.bme.mit.theta.analysis.State -class MultiPartialOrd> -( - private val leftPartOrd: PartialOrd, - private val leftCombineStates: (LControl, DataState) -> LState, - private val rightPartOrd: PartialOrd, - private val rightCombineStates: (RControl, DataState) -> RState, +class MultiPartialOrd< + LState : State, + RState : State, + DataState : State, + LControl : State, + RControl : State, + MState : MultiState, +>( + private val leftPartOrd: PartialOrd, + private val leftCombineStates: (LControl, DataState) -> LState, + private val rightPartOrd: PartialOrd, + private val rightCombineStates: (RControl, DataState) -> RState, ) : PartialOrd { - override fun isLeq(state1: MState, state2: MState): Boolean { - return (leftPartOrd.isLeq( - leftCombineStates(state1.leftState, state1.dataState), - leftCombineStates(state2.leftState, state2.dataState) - ) - && rightPartOrd.isLeq( - rightCombineStates(state1.rightState, state1.dataState), - rightCombineStates(state2.rightState, state2.dataState) - ) - && ((!state1.isSourceMatteringInEquality && !state2.isSourceMatteringInEquality) || (state1.sourceSide == state2.sourceSide))); - } -} \ No newline at end of file + override fun isLeq(state1: MState, state2: MState): Boolean { + return (leftPartOrd.isLeq( + leftCombineStates(state1.leftState, state1.dataState), + leftCombineStates(state2.leftState, state2.dataState), + ) && + rightPartOrd.isLeq( + rightCombineStates(state1.rightState, state1.dataState), + rightCombineStates(state2.rightState, state2.dataState), + ) && + ((!state1.isSourceMatteringInEquality && !state2.isSourceMatteringInEquality) || + (state1.sourceSide == state2.sourceSide))) + } +} diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiTransFunc.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiTransFunc.kt index ffe72c9757..6da7f84e06 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiTransFunc.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/MultiTransFunc.kt @@ -20,48 +20,73 @@ import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.TransFunc -class MultiTransFunc, MAction : MultiAction> -( - private val defineNextSide: (MState) -> MultiSide, - private val createState: (LControl, RControl, DataState, MultiSide) -> MState, - private val leftTransFunc: TransFunc, - private val leftCombineStates: (LControl, DataState) -> LState, - private val leftExtractControlState: (LState) -> LControl, - private val leftExtractDataState: (LState) -> DataState, - private val rightTransFunc: TransFunc, - private val rightCombineStates: (RControl, DataState) -> RState, - private val rightExtractControlState: (RState) -> RControl, - private val rightExtractDataState: (RState) -> DataState, +class MultiTransFunc< + LState : State, + RState : State, + DataState : State, + LControl : State, + RControl : State, + LAction : Action, + RAction : Action, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + MState : MultiState, + MAction : MultiAction, +>( + private val defineNextSide: (MState) -> MultiSide, + private val createState: (LControl, RControl, DataState, MultiSide) -> MState, + private val leftTransFunc: TransFunc, + private val leftCombineStates: (LControl, DataState) -> LState, + private val leftExtractControlState: (LState) -> LControl, + private val leftExtractDataState: (LState) -> DataState, + private val rightTransFunc: TransFunc, + private val rightCombineStates: (RControl, DataState) -> RState, + private val rightExtractControlState: (RState) -> RControl, + private val rightExtractDataState: (RState) -> DataState, ) : TransFunc> { - override fun getSuccStates( - state: MState, action: MAction, prec: MultiPrec? - ): MutableCollection { - val nextSide: MultiSide = defineNextSide(state) - val succStates: MutableList = mutableListOf() - if (nextSide != MultiSide.RIGHT && action.leftAction != null) { - leftTransFunc.getSuccStates( - leftCombineStates(state.leftState, state.dataState), action.leftAction, prec!!.leftPrec - ).map { - createState( - leftExtractControlState(it!!), state.rightState, leftExtractDataState(it), - MultiSide.LEFT - ) - }.forEach(succStates::add) + override fun getSuccStates( + state: MState, + action: MAction, + prec: MultiPrec?, + ): MutableCollection { + val nextSide: MultiSide = defineNextSide(state) + val succStates: MutableList = mutableListOf() + if (nextSide != MultiSide.RIGHT && action.leftAction != null) { + leftTransFunc + .getSuccStates( + leftCombineStates(state.leftState, state.dataState), + action.leftAction, + prec!!.leftPrec, + ) + .map { + createState( + leftExtractControlState(it!!), + state.rightState, + leftExtractDataState(it), + MultiSide.LEFT, + ) } - if (nextSide != MultiSide.LEFT && action.rightAction != null) { - rightTransFunc.getSuccStates( - rightCombineStates(state.rightState, state.dataState), action.rightAction, prec!!.rightPrec - ).map { - createState( - state.leftState, rightExtractControlState(it!!), rightExtractDataState(it), - MultiSide.RIGHT - ) - }.forEach(succStates::add) + .forEach(succStates::add) + } + if (nextSide != MultiSide.LEFT && action.rightAction != null) { + rightTransFunc + .getSuccStates( + rightCombineStates(state.rightState, state.dataState), + action.rightAction, + prec!!.rightPrec, + ) + .map { + createState( + state.leftState, + rightExtractControlState(it!!), + rightExtractDataState(it), + MultiSide.RIGHT, + ) } - return succStates + .forEach(succStates::add) } + return succStates + } } diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiBuilderResult.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiBuilderResult.kt index eea37fb1ce..3b4ec4b3f0 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiBuilderResult.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiBuilderResult.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.multi.builder import hu.bme.mit.theta.analysis.Action @@ -24,45 +23,126 @@ import hu.bme.mit.theta.analysis.unit.UnitPrec import hu.bme.mit.theta.analysis.unit.UnitState /** - * TODO - * The following class in this file should be used, however, with - * IntelliJ IDEA 2024.1 (Ultimate Edition) - * Build #IU-241.14494.240, built on March 28, 2024 - * Kotlin: 241.14494.240-IJ - * the usage of this class causes the kotlin plugin to crash, maybe even crashing - * the whole IDE. For this reason, a POJO has to be used instead + * TODO The following class in this file should be used, however, with IntelliJ IDEA 2024.1 + * (Ultimate Edition) Build #IU-241.14494.240, built on March 28, 2024 Kotlin: 241.14494.240-IJ the + * usage of this class causes the kotlin plugin to crash, maybe even crashing the whole IDE. For + * this reason, a POJO has to be used instead */ -private data class MultiBuilderResult, MControlState : MultiState, MAction : MultiAction, MLts : MultiLts> -( - val side: MultiAnalysisSide, MultiPrec>, - val lts: MLts +private data class MultiBuilderResult< + LState : State, + RState : State, + DataState : State, + LControl : State, + RControl : State, + LAction : Action, + RAction : Action, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, + MState : MultiState, + MControlState : MultiState, + MAction : MultiAction, + MLts : MultiLts, +>( + val side: + MultiAnalysisSide< + MState, + DataState, + MControlState, + MAction, + MultiPrec, + MultiPrec, + >, + val lts: MLts, ) -internal fun , MControlState : MultiState, MAction : MultiAction, MAnalysis : MultiAnalysis, MLts : MultiLts> multiBuilderResult( - analysis: MAnalysis, lts: MLts -): MultiBuilderResultPOJO { - val initFunc = MultiControlInitFunc( - analysis.leftSide.controlInitFunc, analysis.rightSide.controlInitFunc, analysis::createControlInitState +internal fun < + LState : State, + RState : State, + DataState : State, + LControl : State, + RControl : State, + LAction : Action, + RAction : Action, + LPrec : Prec, + RPrec : Prec, + DataPrec : Prec, + LControlPrec : Prec, + RControlPrec : Prec, + MState : MultiState, + MControlState : MultiState, + MAction : MultiAction, + MAnalysis : MultiAnalysis< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + MState, + MControlState, + MAction, + >, + MLts : MultiLts, +> multiBuilderResult( + analysis: MAnalysis, + lts: MLts, +): MultiBuilderResultPOJO< + LState, + RState, + DataState, + LControl, + RControl, + LAction, + RAction, + LPrec, + RPrec, + DataPrec, + LControlPrec, + RControlPrec, + MState, + MControlState, + MAction, + MLts, +> { + val initFunc = + MultiControlInitFunc( + analysis.leftSide.controlInitFunc, + analysis.rightSide.controlInitFunc, + analysis::createControlInitState, ) - val combineStates: (MControlState, DataState) -> MState = { controlS, dataS -> - analysis.createState(controlS.leftState, controlS.rightState, dataS, controlS.sourceSide) - } - val extractControlState: (MState) -> MControlState = { mState -> - analysis.createControlState(mState.leftState, mState.rightState, mState.sourceSide) - } - val extractDataFromState: (MState) -> DataState = { mState -> - mState.dataState + val combineStates: (MControlState, DataState) -> MState = { controlS, dataS -> + analysis.createState(controlS.leftState, controlS.rightState, dataS, controlS.sourceSide) + } + val extractControlState: (MState) -> MControlState = { mState -> + analysis.createControlState(mState.leftState, mState.rightState, mState.sourceSide) + } + val extractDataFromState: (MState) -> DataState = { mState -> mState.dataState } + val extractControlPrec: + (MultiPrec) -> MultiPrec = + { mPrec -> + MultiPrec( + analysis.leftSide.extractControlPrec.invoke(mPrec.leftPrec()), + analysis.rightSide.extractControlPrec.invoke(mPrec.rightPrec()), + UnitPrec.getInstance(), + ) } - val extractControlPrec: (MultiPrec) -> MultiPrec = - { mPrec -> - MultiPrec( - analysis.leftSide.extractControlPrec.invoke( - mPrec.leftPrec() - ), analysis.rightSide.extractControlPrec.invoke(mPrec.rightPrec()), UnitPrec.getInstance() - ) - } - val side = MultiAnalysisSide( - analysis, initFunc, combineStates, extractControlState, extractDataFromState, extractControlPrec + val side = + MultiAnalysisSide( + analysis, + initFunc, + combineStates, + extractControlState, + extractDataFromState, + extractControlPrec, ) - return MultiBuilderResultPOJO(side, lts) -} \ No newline at end of file + return MultiBuilderResultPOJO(side, lts) +} diff --git a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiControlInitFunc.kt b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiControlInitFunc.kt index d094692eac..63284de417 100644 --- a/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiControlInitFunc.kt +++ b/subprojects/common/analysis/src/main/kotlin/hu/bme/mit/theta/analysis/multi/builder/MultiControlInitFunc.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.multi.builder import hu.bme.mit.theta.analysis.InitFunc @@ -25,23 +24,29 @@ import hu.bme.mit.theta.analysis.unit.UnitPrec import hu.bme.mit.theta.analysis.unit.UnitState /** - * Serves as a control initial function for a multi analysis if the product is nested and this analysis is going to be a part of a larger product. + * Serves as a control initial function for a multi analysis if the product is nested and this + * analysis is going to be a part of a larger product. */ -internal class MultiControlInitFunc, MPrec : MultiPrec> -( - private val leftControlInitFunc: InitFunc, - private val rightControlInitFunc: InitFunc, - private val createState: (lState: LControl, rState: RControl) -> MState +internal class MultiControlInitFunc< + LControl : State, + RControl : State, + LControlPrec : Prec, + RControlPrec : Prec, + MState : MultiState, + MPrec : MultiPrec, +>( + private val leftControlInitFunc: InitFunc, + private val rightControlInitFunc: InitFunc, + private val createState: (lState: LControl, rState: RControl) -> MState, ) : InitFunc { - override fun getInitStates(prec: MPrec): MutableCollection { - val leftInitStates: Collection = HashSet(leftControlInitFunc.getInitStates(prec.leftPrec)) - val rightInitStates: Collection = HashSet(rightControlInitFunc.getInitStates(prec.rightPrec)) - return leftInitStates.flatMap { left -> - rightInitStates.map { right -> - createState(left, right) - } - }.toMutableSet() - - } -} \ No newline at end of file + override fun getInitStates(prec: MPrec): MutableCollection { + val leftInitStates: Collection = + HashSet(leftControlInitFunc.getInitStates(prec.leftPrec)) + val rightInitStates: Collection = + HashSet(rightControlInitFunc.getInitStates(prec.rightPrec)) + return leftInitStates + .flatMap { left -> rightInitStates.map { right -> createState(left, right) } } + .toMutableSet() + } +} diff --git a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/BoundedTest.kt b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/BoundedTest.kt index c1a49e333e..c02a0756a7 100644 --- a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/BoundedTest.kt +++ b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/algorithm/BoundedTest.kt @@ -34,67 +34,71 @@ import org.junit.Test class BoundedTest { - companion object { + companion object { - private var unsafeMonolithicExpr: MonolithicExpr? = null - private var safeMonolithicExpr: MonolithicExpr? = null - private val valToState = { valuation: Valuation -> - ExprStateStub(valuation.toExpr()) - } - private val biValToAction = { valuation: Valuation?, valuation2: Valuation? -> - ExprActionStub( - emptyList()) - } - - init { - val x = Decls.Var("x", Int()) - val unfoldResult = StmtUtils.toExpr(Assign(x, IntExprs.Add(x.ref, Int(1))), VarIndexingFactory.indexing(0)) - unsafeMonolithicExpr = MonolithicExpr( - AbstractExprs.Eq(x.ref, Int(0)), - And(unfoldResult.exprs), - AbstractExprs.Neq(x.ref, Int(5)), - unfoldResult.indexing - ) - safeMonolithicExpr = MonolithicExpr( - AbstractExprs.Eq(x.ref, Int(0)), - And(unfoldResult.exprs), - AbstractExprs.Neq(x.ref, Int(-5)), - unfoldResult.indexing - ) - } + private var unsafeMonolithicExpr: MonolithicExpr? = null + private var safeMonolithicExpr: MonolithicExpr? = null + private val valToState = { valuation: Valuation -> ExprStateStub(valuation.toExpr()) } + private val biValToAction = { valuation: Valuation?, valuation2: Valuation? -> + ExprActionStub(emptyList()) } - @Test - fun testBoundedUnsafe() { - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val itpSolver = Z3LegacySolverFactory.getInstance().createItpSolver() - val indSolver = Z3LegacySolverFactory.getInstance().createSolver() - val checker: BoundedChecker<*, *> = BoundedChecker( - monolithicExpr = unsafeMonolithicExpr!!, - bmcSolver = solver, - itpSolver = itpSolver, - indSolver = indSolver, - valToState = valToState, - biValToAction = biValToAction, - logger = ConsoleLogger(Logger.Level.VERBOSE)) - val safetyResult: SafetyResult<*, *> = checker.check() - Assert.assertTrue(safetyResult.isUnsafe()) + init { + val x = Decls.Var("x", Int()) + val unfoldResult = + StmtUtils.toExpr(Assign(x, IntExprs.Add(x.ref, Int(1))), VarIndexingFactory.indexing(0)) + unsafeMonolithicExpr = + MonolithicExpr( + AbstractExprs.Eq(x.ref, Int(0)), + And(unfoldResult.exprs), + AbstractExprs.Neq(x.ref, Int(5)), + unfoldResult.indexing, + ) + safeMonolithicExpr = + MonolithicExpr( + AbstractExprs.Eq(x.ref, Int(0)), + And(unfoldResult.exprs), + AbstractExprs.Neq(x.ref, Int(-5)), + unfoldResult.indexing, + ) } + } - @Test - fun testBoundedSafe() { - val solver = Z3LegacySolverFactory.getInstance().createSolver() - val itpSolver = Z3LegacySolverFactory.getInstance().createItpSolver() - val indSolver = Z3LegacySolverFactory.getInstance().createSolver() - val checker: BoundedChecker<*, *> = BoundedChecker( - monolithicExpr = safeMonolithicExpr!!, - bmcSolver = solver, - itpSolver = itpSolver, - indSolver = indSolver, - valToState = valToState, - biValToAction = biValToAction, - logger = ConsoleLogger(Logger.Level.VERBOSE)) - val safetyResult: SafetyResult<*, *> = checker.check() - Assert.assertTrue(safetyResult.isSafe()) - } + @Test + fun testBoundedUnsafe() { + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val itpSolver = Z3LegacySolverFactory.getInstance().createItpSolver() + val indSolver = Z3LegacySolverFactory.getInstance().createSolver() + val checker: BoundedChecker<*, *> = + BoundedChecker( + monolithicExpr = unsafeMonolithicExpr!!, + bmcSolver = solver, + itpSolver = itpSolver, + indSolver = indSolver, + valToState = valToState, + biValToAction = biValToAction, + logger = ConsoleLogger(Logger.Level.VERBOSE), + ) + val safetyResult: SafetyResult<*, *> = checker.check() + Assert.assertTrue(safetyResult.isUnsafe()) + } + + @Test + fun testBoundedSafe() { + val solver = Z3LegacySolverFactory.getInstance().createSolver() + val itpSolver = Z3LegacySolverFactory.getInstance().createItpSolver() + val indSolver = Z3LegacySolverFactory.getInstance().createSolver() + val checker: BoundedChecker<*, *> = + BoundedChecker( + monolithicExpr = safeMonolithicExpr!!, + bmcSolver = solver, + itpSolver = itpSolver, + indSolver = indSolver, + valToState = valToState, + biValToAction = biValToAction, + logger = ConsoleLogger(Logger.Level.VERBOSE), + ) + val safetyResult: SafetyResult<*, *> = checker.check() + Assert.assertTrue(safetyResult.isSafe()) + } } diff --git a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysisTest.kt b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysisTest.kt index 9379846676..7d51fc7de8 100644 --- a/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysisTest.kt +++ b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysisTest.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.analysis.ptr import hu.bme.mit.theta.analysis.expl.ExplAnalysis @@ -35,47 +34,45 @@ import org.junit.jupiter.params.provider.MethodSource class PtrAnalysisTest { - companion object { + companion object { - private val x = Var("x", Int()) + private val x = Var("x", Int()) - private val explTop0 = PtrState(ExplState.top(), nextCnt = 0) - private val explTop1 = PtrState(ExplState.top(), nextCnt = 1) + private val explTop0 = PtrState(ExplState.top(), nextCnt = 0) + private val explTop1 = PtrState(ExplState.top(), nextCnt = 1) - private val emptyAction = PtrActionStub(listOf(), emptyMap()) - private val readLiteralOnly = PtrActionStub(listOf(Assume(Eq(Dereference(Int(0), Int(1), Int()), Int(0)))), - emptyMap()) - private val writeLiteralOnly = PtrActionStub(listOf(MemoryAssign(Dereference(Int(0), Int(1), Int()), Int(0))), - emptyMap()) + private val emptyAction = PtrActionStub(listOf(), emptyMap()) + private val readLiteralOnly = + PtrActionStub(listOf(Assume(Eq(Dereference(Int(0), Int(1), Int()), Int(0)))), emptyMap()) + private val writeLiteralOnly = + PtrActionStub(listOf(MemoryAssign(Dereference(Int(0), Int(1), Int()), Int(0))), emptyMap()) - private val emptyPrec = PtrPrec(ExplPrec.empty(), emptySet()) + private val emptyPrec = PtrPrec(ExplPrec.empty(), emptySet()) - @JvmStatic - fun testInputs(): Collection { - return listOf( - Arguments.of(explTop0, emptyAction, emptyPrec, - listOf(explTop0)), - Arguments.of(explTop0, readLiteralOnly, emptyPrec, - listOf(explTop1)), - Arguments.of(explTop0, writeLiteralOnly, emptyPrec, - listOf( - PtrState(ExplState.top(), 1))), - ) - } + @JvmStatic + fun testInputs(): Collection { + return listOf( + Arguments.of(explTop0, emptyAction, emptyPrec, listOf(explTop0)), + Arguments.of(explTop0, readLiteralOnly, emptyPrec, listOf(explTop1)), + Arguments.of(explTop0, writeLiteralOnly, emptyPrec, listOf(PtrState(ExplState.top(), 1))), + ) } + } - @ParameterizedTest - @MethodSource("testInputs") - fun transFuncTest(state: PtrState, action: PtrAction, prec: PtrPrec, - expectedResult: Collection>) { - val analysis = - PtrAnalysis(ExplAnalysis.create(Z3LegacySolverFactory.getInstance().createSolver(), True())) - val result = analysis.transFunc.getSuccStates(state, action, prec) - Assertions.assertEquals(expectedResult.toSet(), result.toSet()) - } + @ParameterizedTest + @MethodSource("testInputs") + fun transFuncTest( + state: PtrState, + action: PtrAction, + prec: PtrPrec, + expectedResult: Collection>, + ) { + val analysis = + PtrAnalysis(ExplAnalysis.create(Z3LegacySolverFactory.getInstance().createSolver(), True())) + val result = analysis.transFunc.getSuccStates(state, action, prec) + Assertions.assertEquals(expectedResult.toSet(), result.toSet()) + } } data class PtrActionStub(override val stmtList: List, val writeTriples: WriteTriples) : - PtrAction(writeTriples, 0) { - -} \ No newline at end of file + PtrAction(writeTriples, 0) {} diff --git a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/logging/Logger.java b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/logging/Logger.java index 674ca49327..6cd2229ccb 100644 --- a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/logging/Logger.java +++ b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/logging/Logger.java @@ -15,26 +15,30 @@ */ package hu.bme.mit.theta.common.logging; -/** - * Interface for logging within algorithms. - */ +/** Interface for logging within algorithms. */ public interface Logger { - /** - * Detailedness of logging in order. - */ + /** Detailedness of logging in order. */ public enum Level { - RESULT, MAINSTEP, SUBSTEP, INFO, DETAIL, VERBOSE + RESULT, + MAINSTEP, + SUBSTEP, + INFO, + DETAIL, + VERBOSE } /** * Write objects with a given level and pattern * - * @param level Level + * @param level Level * @param pattern Pattern for {@link String#format(String, Object...)} * @param objects Objects to be substituted in the pattern * @return Logger instance */ Logger write(Level level, String pattern, Object... objects); + default Logger writeln(Level level, String pattern, Object... objects) { + return write(level, pattern + "%n", objects); + } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt index 5b7789b350..efd0e2917d 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/ChcUtils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.core import com.google.common.base.Preconditions.checkArgument @@ -26,6 +25,7 @@ import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.core.type.functype.FuncExprs import hu.bme.mit.theta.core.type.functype.FuncType +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.utils.ExprUtils import java.util.* @@ -41,75 +41,97 @@ head_name(grounds) // facts */ open class Relation(val name: String, vararg paramTypes: Type) { - companion object { - - private fun funcType(params: List, finalType: Type): FuncType<*, *> { - return if (params.size == 1) { - FuncType.of(params[0], finalType) - } else if (params.size > 1) { - FuncType.of(params[0], funcType(params.subList(1, params.size), finalType)) - } else { - error("Nullary functions aren't handled here.") - } - } + companion object { + + private fun funcType(params: List, finalType: Type): FuncType<*, *> { + return if (params.size == 1) { + FuncType.of(params[0], finalType) + } else if (params.size > 1) { + FuncType.of(params[0], funcType(params.subList(1, params.size), finalType)) + } else { + error("Nullary functions aren't handled here.") + } } + } - val arity: Int = paramTypes.size - val rules: MutableList = LinkedList() - val constDecl = if (arity == 0) Const(name, Bool()) else Const(name, funcType(paramTypes.toList(), Bool())) - open operator fun invoke(params: List>) = RelationApp(this, params) - open operator fun invoke(vararg params: Expr<*>) = RelationApp(this, params.toList()) -} + val arity: Int = paramTypes.size + val rules: MutableList = LinkedList() + val constDecl = Const(name, funcType(paramTypes.toList(), Bool())) -data class RelationApp(val relation: Relation, val params: List>, - val constraints: List> = emptyList()) { + open operator fun invoke(params: List>) = RelationApp(this, params) - init { - checkArgument(params.size == relation.arity) - } + open operator fun invoke(vararg params: Expr<*>) = RelationApp(this, params.toList()) +} - val expr: Expr by lazy { - val coreExpr = if (params.size >= 1) { - FuncExprs.App(relation.constDecl.ref as Expr>, params.map { it }) - } else { - relation.constDecl.ref as Expr - } - if (constraints.isEmpty()) { - coreExpr - } else { - And(constraints + coreExpr) - } +data class RelationApp( + val relation: Relation, + val params: List>, + val constraints: List> = emptyList(), +) { + + init { + checkArgument(params.size == relation.arity) + } + + val expr: Expr by lazy { + val coreExpr = + if (params.size >= 1) { + FuncExprs.App( + relation.constDecl.ref as Expr>, + params.map { it }, + ) + } else { + relation.constDecl.ref as Expr + } + if (constraints.isEmpty()) { + coreExpr + } else { + And(constraints + coreExpr) } + } - operator fun plusAssign(constraints: List>) { - relation.rules.add(Rule(expr, constraints)) - } + operator fun plusAssign(constraints: List>) { + relation.rules.add(Rule(expr, constraints)) + } - operator fun plusAssign(constraint: Expr) { - relation.rules.add(Rule(expr, listOf(constraint))) - } + operator fun plusAssign(constraint: Expr) { + relation.rules.add(Rule(expr, listOf(constraint))) + } - operator fun not() { - relation.rules.add(Rule(False(), listOf(expr))) - } + operator fun not() { + relation.rules.add(Rule(False(), listOf(expr))) + } - operator fun unaryPlus() { - relation.rules.add(Rule(expr, listOf())) - } + operator fun unaryPlus() { + relation.rules.add(Rule(expr, listOf())) + } + + infix fun with(constraints: List>) = + copy(constraints = this.constraints + constraints) - infix fun with(constraints: List>) = copy(constraints = this.constraints + constraints) - infix fun with(constraint: Expr) = copy(constraints = this.constraints + constraint) + infix fun with(constraint: Expr) = copy(constraints = this.constraints + constraint) } data class Rule(val head: Expr, val constraints: List>) { - fun toExpr() = Forall(ExprUtils.getParams(head) + ExprUtils.getParams(constraints), Imply(And(constraints), head)) + fun toExpr(): Expr { + val params = ExprUtils.getParams(head) + ExprUtils.getParams(constraints) + val nontrivialConstraints = constraints.filter { it != True() } + return if (params.isNotEmpty()) { + Forall(params, Imply(And(nontrivialConstraints), head)) + } else if (nontrivialConstraints.isNotEmpty()) { + Forall(listOf(Param("__dummy__", Int())), Imply(And(nontrivialConstraints), head)) + } else { + head + } + } } operator fun Expr.plus(other: Expr) = listOf(this, other) data class ParamHolder(private val type: T) { - private val lookup = LinkedHashMap>() - operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref -} \ No newline at end of file + private val lookup = LinkedHashMap>() + + operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/Utils.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/Utils.kt index 488fe3330b..7b141fbb09 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/Utils.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/Utils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar import org.antlr.v4.runtime.BaseErrorListener @@ -24,17 +23,23 @@ import org.antlr.v4.runtime.misc.Interval import org.antlr.v4.runtime.misc.ParseCancellationException fun ParserRuleContext.textWithWS(): String { - val a: Int = start.startIndex - val b: Int = stop.stopIndex - val interval = Interval(a, b) - return start.inputStream.getText(interval) + val a: Int = start.startIndex + val b: Int = stop.stopIndex + val interval = Interval(a, b) + return start.inputStream.getText(interval) } object ThrowingErrorListener : BaseErrorListener() { - @Throws(ParseCancellationException::class) - override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, - charPositionInLine: Int, msg: String, e: RecognitionException?) { - throw ParseCancellationException("line $line:$charPositionInLine $msg") - } -} \ No newline at end of file + @Throws(ParseCancellationException::class) + override fun syntaxError( + recognizer: Recognizer<*, *>?, + offendingSymbol: Any?, + line: Int, + charPositionInLine: Int, + msg: String, + e: RecognitionException?, + ) { + throw ParseCancellationException("line $line:$charPositionInLine $msg") + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/expr/ExprParser.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/expr/ExprParser.kt index f896e6265a..d497a3f45d 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/expr/ExprParser.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/expr/ExprParser.kt @@ -64,844 +64,883 @@ import hu.bme.mit.theta.grammar.dsl.gen.ExprParser import hu.bme.mit.theta.grammar.dsl.gen.ExprParser.* import hu.bme.mit.theta.grammar.dsl.type.TypeWrapper import hu.bme.mit.theta.grammar.textWithWS -import org.antlr.v4.runtime.BailErrorStrategy -import org.antlr.v4.runtime.CharStreams -import org.antlr.v4.runtime.CommonTokenStream -import org.antlr.v4.runtime.Token import java.math.BigInteger import java.util.* import java.util.function.Function import java.util.regex.Pattern import java.util.stream.Collectors import java.util.stream.Stream +import org.antlr.v4.runtime.BailErrorStrategy +import org.antlr.v4.runtime.CharStreams +import org.antlr.v4.runtime.CommonTokenStream +import org.antlr.v4.runtime.Token class ExpressionWrapper(scope: Scope, content: String) { - private val scope: Scope - private val context: ExprContext + private val scope: Scope + private val context: ExprContext + + init { + this.scope = Preconditions.checkNotNull(scope) + val lexer = ExprLexer(CharStreams.fromString(content)) + lexer.addErrorListener(ThrowingErrorListener) + val parser = ExprParser(CommonTokenStream(lexer)) + parser.errorHandler = BailErrorStrategy() + this.context = Preconditions.checkNotNull(parser.expr()) + } + + fun instantiate(env: Env): Expr { + val visitor = ExprCreatorVisitor(scope, env) + val expr: Expr<*> = context.accept>(visitor) + return expr + } + + private class ExprCreatorVisitor(scope: Scope, env: Env) : ExprBaseVisitor>() { + + private var currentScope: Scope + private val env: Env init { - this.scope = Preconditions.checkNotNull(scope) - val lexer = ExprLexer(CharStreams.fromString(content)) - lexer.addErrorListener(ThrowingErrorListener) - val parser = ExprParser(CommonTokenStream(lexer)) - parser.errorHandler = BailErrorStrategy() - this.context = Preconditions.checkNotNull(parser.expr()) + currentScope = Preconditions.checkNotNull(scope) + this.env = Preconditions.checkNotNull(env) } - fun instantiate(env: Env): Expr { - val visitor = ExprCreatorVisitor(scope, env) - val expr: Expr<*> = context.accept>(visitor) - return expr + //// + private fun push(paramDecls: List>) { + val scope = BasicScope(currentScope) + env.push() + for (paramDecl in paramDecls) { + val symbol: Symbol = DeclSymbol.of(paramDecl) + scope.declare(symbol) + env.define(symbol, paramDecl) + } + currentScope = scope } - private class ExprCreatorVisitor(scope: Scope, env: Env) : ExprBaseVisitor>() { + private fun pop() { + Preconditions.checkState( + currentScope.enclosingScope().isPresent, + "Enclosing scope is not present.", + ) + currentScope = currentScope.enclosingScope().get() + env.pop() + } - private var currentScope: Scope - private val env: Env + //// + override fun visitFuncLitExpr(ctx: FuncLitExprContext): Expr { + return if (ctx.result != null) { + val param = + Decls.Param(ctx.param.name.text, TypeWrapper(ctx.param.type().textWithWS()).instantiate()) + push(listOf(param)) + val result = ctx.result.accept>(this) as Expr + pop() + FuncExprs.Func(param, result) + } else { + visitChildren(ctx) + } + } - init { - currentScope = Preconditions.checkNotNull(scope) - this.env = Preconditions.checkNotNull(env) - } + private fun createParamList(ctx: DeclListContext): List> { + return if (ctx.decls == null) { + emptyList() + } else { + ctx.decls + .stream() + .map { d: DeclContext -> + Decls.Param(d.name.getText(), TypeWrapper(d.ttype.textWithWS()).instantiate()) + } + .collect(Collectors.toList()) + } + } - //// - private fun push(paramDecls: List>) { - val scope = BasicScope(currentScope) - env.push() - for (paramDecl in paramDecls) { - val symbol: Symbol = DeclSymbol.of(paramDecl) - scope.declare(symbol) - env.define(symbol, paramDecl) - } - currentScope = scope - } + //// + override fun visitIteExpr(ctx: IteExprContext): Expr { + return if (ctx.cond != null) { + val cond: Expr = TypeUtils.cast(ctx.cond.accept>(this), BoolExprs.Bool()) + val then: Expr<*> = ctx.then.accept>(this) + val elze: Expr<*> = ctx.elze.accept>(this) + AbstractExprs.Ite(cond, then, elze) + } else { + visitChildren(ctx) + } + } - private fun pop() { - Preconditions.checkState(currentScope.enclosingScope().isPresent, - "Enclosing scope is not present.") - currentScope = currentScope.enclosingScope().get() - env.pop() - } + override fun visitIffExpr(ctx: IffExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp: Expr = + TypeUtils.cast(ctx.leftOp.accept>(this), BoolExprs.Bool()) + val rightOp: Expr = + TypeUtils.cast(ctx.rightOp.accept>(this), BoolExprs.Bool()) + BoolExprs.Iff(leftOp, rightOp) + } else { + visitChildren(ctx) + } + } - //// - override fun visitFuncLitExpr(ctx: FuncLitExprContext): Expr { - return if (ctx.result != null) { - val param = Decls.Param(ctx.param.name.text, - TypeWrapper(ctx.param.type().textWithWS()).instantiate()) - push(listOf(param)) - val result = ctx.result.accept>(this) as Expr - pop() - FuncExprs.Func(param, result) - } else { - visitChildren(ctx) - } - } + override fun visitImplyExpr(ctx: ImplyExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp: Expr = + TypeUtils.cast(ctx.leftOp.accept>(this), BoolExprs.Bool()) + val rightOp: Expr = + TypeUtils.cast(ctx.rightOp.accept>(this), BoolExprs.Bool()) + BoolExprs.Imply(leftOp, rightOp) + } else { + visitChildren(ctx) + } + } - private fun createParamList(ctx: DeclListContext): List> { - return if (ctx.decls == null) { - emptyList() - } else { - ctx.decls.stream() - .map { d: DeclContext -> - Decls.Param(d.name.getText(), - TypeWrapper(d.ttype.textWithWS()).instantiate()) - }.collect(Collectors.toList()) - } - } + override fun visitForallExpr(ctx: ForallExprContext): Expr { + return if (ctx.paramDecls != null) { + val paramDecls = createParamList(ctx.paramDecls) + push(paramDecls) + val op: Expr = TypeUtils.cast(ctx.op.accept>(this), BoolExprs.Bool()) + pop() + BoolExprs.Forall(paramDecls, op) + } else { + visitChildren(ctx) + } + } - //// - override fun visitIteExpr(ctx: IteExprContext): Expr { - return if (ctx.cond != null) { - val cond: Expr = TypeUtils.cast(ctx.cond.accept>(this), - BoolExprs.Bool()) - val then: Expr<*> = ctx.then.accept>(this) - val elze: Expr<*> = ctx.elze.accept>(this) - AbstractExprs.Ite(cond, then, elze) - } else { - visitChildren(ctx) - } - } + override fun visitExistsExpr(ctx: ExistsExprContext): Expr { + return if (ctx.paramDecls != null) { + val paramDecls = createParamList(ctx.paramDecls) + push(paramDecls) + val op: Expr = TypeUtils.cast(ctx.op.accept>(this), BoolExprs.Bool()) + pop() + BoolExprs.Exists(paramDecls, op) + } else { + visitChildren(ctx) + } + } - override fun visitIffExpr(ctx: IffExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp: Expr = TypeUtils.cast(ctx.leftOp.accept>(this), - BoolExprs.Bool()) - val rightOp: Expr = TypeUtils.cast(ctx.rightOp.accept>(this), - BoolExprs.Bool()) - BoolExprs.Iff(leftOp, rightOp) - } else { - visitChildren(ctx) - } - } + override fun visitOrExpr(ctx: OrExprContext): Expr { + return if (ctx.ops.size >= 1) { + val opStream: Stream> = + ctx.ops + .stream() + .map({ op: ExprContext -> TypeUtils.cast(op.accept>(this), BoolExprs.Bool()) }) + val ops: Collection> = opStream.collect(Collectors.toList()) + BoolExprs.Or(ops) + } else { + visitChildren(ctx) + } + } - override fun visitImplyExpr(ctx: ImplyExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp: Expr = TypeUtils.cast(ctx.leftOp.accept>(this), - BoolExprs.Bool()) - val rightOp: Expr = TypeUtils.cast(ctx.rightOp.accept>(this), - BoolExprs.Bool()) - BoolExprs.Imply(leftOp, rightOp) - } else { - visitChildren(ctx) - } - } + override fun visitXorExpr(ctx: XorExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp: Expr = + TypeUtils.cast(ctx.leftOp.accept>(this), BoolExprs.Bool()) + val rightOp: Expr = + TypeUtils.cast(ctx.rightOp.accept>(this), BoolExprs.Bool()) + BoolExprs.Xor(leftOp, rightOp) + } else { + visitChildren(ctx) + } + } - override fun visitForallExpr(ctx: ForallExprContext): Expr { - return if (ctx.paramDecls != null) { - val paramDecls = createParamList(ctx.paramDecls) - push(paramDecls) - val op: Expr = TypeUtils.cast(ctx.op.accept>(this), - BoolExprs.Bool()) - pop() - BoolExprs.Forall(paramDecls, op) - } else { - visitChildren(ctx) - } - } + override fun visitAndExpr(ctx: AndExprContext): Expr { + return if (ctx.ops.size >= 1) { + val opStream: Stream> = + ctx.ops + .stream() + .map({ op: ExprContext -> TypeUtils.cast(op.accept>(this), BoolExprs.Bool()) }) + val ops: Collection> = opStream.collect(Collectors.toList()) + BoolExprs.And(ops) + } else { + visitChildren(ctx) + } + } - override fun visitExistsExpr(ctx: ExistsExprContext): Expr { - return if (ctx.paramDecls != null) { - val paramDecls = createParamList(ctx.paramDecls) - push(paramDecls) - val op: Expr = TypeUtils.cast(ctx.op.accept>(this), - BoolExprs.Bool()) - pop() - BoolExprs.Exists(paramDecls, op) - } else { - visitChildren(ctx) - } - } + override fun visitNotExpr(ctx: NotExprContext): Expr { + return if (ctx.op != null) { + val op: Expr = TypeUtils.cast(ctx.op.accept>(this), BoolExprs.Bool()) + BoolExprs.Not(op) + } else { + visitChildren(ctx) + } + } - override fun visitOrExpr(ctx: OrExprContext): Expr { - return if (ctx.ops.size >= 1) { - val opStream: Stream> = ctx.ops.stream() - .map({ op: ExprContext -> - TypeUtils.cast(op.accept>(this), BoolExprs.Bool()) - }) - val ops: Collection> = opStream.collect(Collectors.toList()) - BoolExprs.Or(ops) - } else { - visitChildren(ctx) - } - } + override fun visitEqualityExpr(ctx: EqualityExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp: Expr<*> = ctx.leftOp.accept>(this) + val rightOp: Expr<*> = ctx.rightOp.accept>(this) + when (ctx.oper.getType()) { + EQ -> AbstractExprs.Eq(leftOp, rightOp) + NEQ -> AbstractExprs.Neq(leftOp, rightOp) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitXorExpr(ctx: XorExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp: Expr = TypeUtils.cast(ctx.leftOp.accept>(this), - BoolExprs.Bool()) - val rightOp: Expr = TypeUtils.cast(ctx.rightOp.accept>(this), - BoolExprs.Bool()) - BoolExprs.Xor(leftOp, rightOp) - } else { - visitChildren(ctx) - } - } + override fun visitRelationExpr(ctx: RelationExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp: Expr<*> = ctx.leftOp.accept>(this) + val rightOp: Expr<*> = ctx.rightOp.accept>(this) + when (ctx.oper.getType()) { + LT -> AbstractExprs.Lt(leftOp, rightOp) + LEQ -> AbstractExprs.Leq(leftOp, rightOp) + GT -> AbstractExprs.Gt(leftOp, rightOp) + GEQ -> AbstractExprs.Geq(leftOp, rightOp) + BV_ULT -> BvExprs.ULt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_ULE -> BvExprs.ULeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_UGT -> BvExprs.UGt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_UGE -> BvExprs.UGeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SLT -> BvExprs.SLt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SLE -> BvExprs.SLeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SGT -> BvExprs.SGt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SGE -> BvExprs.SGeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitAndExpr(ctx: AndExprContext): Expr { - return if (ctx.ops.size >= 1) { - val opStream: Stream> = ctx.ops.stream() - .map({ op: ExprContext -> - TypeUtils.cast(op.accept>(this), BoolExprs.Bool()) - }) - val ops: Collection> = opStream.collect(Collectors.toList()) - BoolExprs.And(ops) - } else { - visitChildren(ctx) - } - } + override fun visitFpFuncExpr(ctx: FpFuncExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp = ctx.leftOp.accept>(this) as Expr + val rightOp = ctx.rightOp.accept>(this) as Expr + when (ctx.oper.getType()) { + FPMAX -> FpExprs.Max(leftOp, rightOp) + FPMIN -> FpExprs.Min(leftOp, rightOp) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitNotExpr(ctx: NotExprContext): Expr { - return if (ctx.op != null) { - val op: Expr = TypeUtils.cast(ctx.op.accept>(this), - BoolExprs.Bool()) - BoolExprs.Not(op) - } else { - visitChildren(ctx) - } - } + //// + override fun visitBitwiseOrExpr(ctx: BitwiseOrExprContext): Expr { + return if (ctx.oper != null) { + val opStream: Stream> = + ctx.ops.stream().map { op: ExprContext -> op.accept>(this) as Expr } + val ops: MutableList> = opStream.collect(Collectors.toList()) + when (ctx.oper.type) { + BV_OR -> BvExprs.Or(ops) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitEqualityExpr(ctx: EqualityExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp: Expr<*> = ctx.leftOp.accept>(this) - val rightOp: Expr<*> = ctx.rightOp.accept>(this) - when (ctx.oper.getType()) { - EQ -> AbstractExprs.Eq(leftOp, rightOp) - NEQ -> AbstractExprs.Neq(leftOp, rightOp) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + override fun visitBitwiseXorExpr(ctx: BitwiseXorExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp = TypeUtils.castBv(ctx.leftOp.accept>(this)) + val rightOp = TypeUtils.castBv(ctx.rightOp.accept>(this)) + when (ctx.oper.getType()) { + BV_XOR -> BvExprs.Xor(java.util.List.of(leftOp, rightOp)) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitRelationExpr(ctx: RelationExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp: Expr<*> = ctx.leftOp.accept>(this) - val rightOp: Expr<*> = ctx.rightOp.accept>(this) - when (ctx.oper.getType()) { - LT -> AbstractExprs.Lt(leftOp, rightOp) - LEQ -> AbstractExprs.Leq(leftOp, rightOp) - GT -> AbstractExprs.Gt(leftOp, rightOp) - GEQ -> AbstractExprs.Geq(leftOp, rightOp) - BV_ULT -> BvExprs.ULt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_ULE -> BvExprs.ULeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_UGT -> BvExprs.UGt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_UGE -> BvExprs.UGeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SLT -> BvExprs.SLt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SLE -> BvExprs.SLeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SGT -> BvExprs.SGt(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SGE -> BvExprs.SGeq(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + override fun visitBitwiseAndExpr(ctx: BitwiseAndExprContext): Expr { + return if (ctx.oper != null) { + val opStream: Stream> = + ctx.ops.stream().map { op: ExprContext -> op.accept>(this) as Expr } + val ops: MutableList> = opStream.collect(Collectors.toList()) + when (ctx.oper.type) { + BV_AND -> BvExprs.And(ops) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - override fun visitFpFuncExpr(ctx: FpFuncExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp = ctx.leftOp.accept>(this) as Expr - val rightOp = ctx.rightOp.accept>(this) as Expr - when (ctx.oper.getType()) { - FPMAX -> FpExprs.Max(leftOp, rightOp) - FPMIN -> FpExprs.Min(leftOp, rightOp) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + override fun visitBitwiseShiftExpr(ctx: BitwiseShiftExprContext): Expr { + return if (ctx.rightOp != null) { + val leftOp = TypeUtils.castBv(ctx.leftOp.accept>(this)) + val rightOp = TypeUtils.castBv(ctx.rightOp.accept>(this)) + when (ctx.oper.getType()) { + BV_SHL -> BvExprs.ShiftLeft(leftOp, rightOp) + BV_ASHR -> BvExprs.ArithShiftRight(leftOp, rightOp) + BV_LSHR -> BvExprs.LogicShiftRight(leftOp, rightOp) + BV_ROL -> BvExprs.RotateLeft(leftOp, rightOp) + BV_ROR -> BvExprs.RotateRight(leftOp, rightOp) + else -> throw ParseException(ctx, "Unknown operator") + } + } else { + visitChildren(ctx) + } + } - //// - override fun visitBitwiseOrExpr(ctx: BitwiseOrExprContext): Expr { - return if (ctx.oper != null) { - val opStream: Stream> = ctx.ops.stream() - .map { op: ExprContext -> op.accept>(this) as Expr } - val ops: MutableList> = opStream.collect(Collectors.toList()) - when (ctx.oper.type) { - BV_OR -> BvExprs.Or(ops) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + //// + override fun visitAdditiveExpr(ctx: AdditiveExprContext): Expr { + return if (ctx.ops.size >= 1) { + val opStream: Stream> = + ctx.ops.stream().map(Function { op: ExprContext -> op.accept>(this) }) + val ops = opStream.collect(Collectors.toList()) + val opsHead = ops[0] + val opsTail = ops.subList(1, ops.size) + createAdditiveExpr(opsHead, opsTail, ctx.oper, ctx) + } else { + visitChildren(ctx) + } + } - override fun visitBitwiseXorExpr(ctx: BitwiseXorExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp = TypeUtils.castBv(ctx.leftOp.accept>(this)) - val rightOp = TypeUtils.castBv(ctx.rightOp.accept>(this)) - when (ctx.oper.getType()) { - BV_XOR -> BvExprs.Xor(java.util.List.of(leftOp, rightOp)) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + private fun createAdditiveExpr( + opsHead: Expr<*>, + opsTail: List>, + oper: Token, + ctx: AdditiveExprContext, + ): Expr { + return if (opsTail.isEmpty()) { + opsHead + } else { + val newOpsHead = opsTail[0] + val newOpsTail = opsTail.subList(1, opsTail.size) + val subExpr = createAdditiveSubExpr(opsHead, newOpsHead, oper, ctx) + createAdditiveExpr(subExpr, newOpsTail, oper, ctx) + } + } - override fun visitBitwiseAndExpr(ctx: BitwiseAndExprContext): Expr { - return if (ctx.oper != null) { - val opStream: Stream> = ctx.ops.stream() - .map { op: ExprContext -> op.accept>(this) as Expr } - val ops: MutableList> = opStream.collect(Collectors.toList()) - when (ctx.oper.type) { - BV_AND -> BvExprs.And(ops) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + private fun createAdditiveSubExpr( + leftOp: Expr<*>, + rightOp: Expr<*>, + oper: Token, + ctx: AdditiveExprContext, + ): Expr { + return when (oper.type) { + PLUS -> createAddExpr(leftOp, rightOp) + MINUS -> createSubExpr(leftOp, rightOp) + BV_ADD -> createBvAddExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SUB -> createBvSubExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + FPADD -> + FpExprs.Add( + getRoundingMode(ctx.oper.text), + java.util.List.of(TypeUtils.castFp(leftOp), TypeUtils.castFp(rightOp)), + ) + + FPSUB -> + FpExprs.Sub( + getRoundingMode(ctx.oper.text), + TypeUtils.castFp(leftOp), + TypeUtils.castFp(rightOp), + ) + + else -> throw ParseException(ctx, "Unknown operator '" + oper.text + "'") + } + } - override fun visitBitwiseShiftExpr(ctx: BitwiseShiftExprContext): Expr { - return if (ctx.rightOp != null) { - val leftOp = TypeUtils.castBv(ctx.leftOp.accept>(this)) - val rightOp = TypeUtils.castBv(ctx.rightOp.accept>(this)) - when (ctx.oper.getType()) { - BV_SHL -> BvExprs.ShiftLeft(leftOp, rightOp) - BV_ASHR -> BvExprs.ArithShiftRight(leftOp, rightOp) - BV_LSHR -> BvExprs.LogicShiftRight(leftOp, rightOp) - BV_ROL -> BvExprs.RotateLeft(leftOp, rightOp) - BV_ROR -> BvExprs.RotateRight(leftOp, rightOp) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + private fun createAddExpr(leftOp: Expr<*>, rightOp: Expr<*>): AddExpr<*> { + return if (leftOp is AddExpr<*>) { + val ops: List> = + ImmutableList.builder>().addAll(leftOp.ops).add(rightOp).build() + AbstractExprs.Add(ops) + } else { + AbstractExprs.Add(leftOp, rightOp) + } + } - //// - override fun visitAdditiveExpr(ctx: AdditiveExprContext): Expr { - return if (ctx.ops.size >= 1) { - val opStream: Stream> = ctx.ops.stream() - .map(Function { op: ExprContext -> op.accept>(this) }) - val ops = opStream.collect(Collectors.toList()) - val opsHead = ops[0] - val opsTail = ops.subList(1, ops.size) - createAdditiveExpr(opsHead, opsTail, ctx.oper, ctx) - } else { - visitChildren(ctx) - } - } + private fun createSubExpr(leftOp: Expr<*>, rightOp: Expr<*>): SubExpr<*> { + return AbstractExprs.Sub(leftOp, rightOp) + } - private fun createAdditiveExpr(opsHead: Expr<*>, opsTail: List>, - oper: Token, ctx: AdditiveExprContext): Expr { - return if (opsTail.isEmpty()) { - opsHead - } else { - val newOpsHead = opsTail[0] - val newOpsTail = opsTail.subList(1, opsTail.size) - val subExpr = createAdditiveSubExpr(opsHead, newOpsHead, oper, ctx) - createAdditiveExpr(subExpr, newOpsTail, oper, ctx) - } - } + private fun createBvAddExpr(leftOp: Expr, rightOp: Expr): BvAddExpr { + return if (leftOp is BvAddExpr) { + val ops: List> = + ImmutableList.builder>().addAll(leftOp.ops).add(rightOp).build() + BvExprs.Add(ops) + } else { + BvExprs.Add(Arrays.asList(leftOp, rightOp)) + } + } - private fun createAdditiveSubExpr(leftOp: Expr<*>, rightOp: Expr<*>, oper: Token, - ctx: AdditiveExprContext): Expr { - return when (oper.type) { - PLUS -> createAddExpr(leftOp, rightOp) - MINUS -> createSubExpr(leftOp, rightOp) - BV_ADD -> createBvAddExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SUB -> createBvSubExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - FPADD -> FpExprs.Add(getRoundingMode(ctx.oper.text), - java.util.List.of(TypeUtils.castFp(leftOp), TypeUtils.castFp(rightOp))) - - FPSUB -> FpExprs.Sub(getRoundingMode(ctx.oper.text), TypeUtils.castFp(leftOp), - TypeUtils.castFp(rightOp)) - - else -> throw ParseException(ctx, "Unknown operator '" + oper.text + "'") - } - } + private fun createBvSubExpr(leftOp: Expr, rightOp: Expr): BvSubExpr { + return BvExprs.Sub(leftOp, rightOp) + } - private fun createAddExpr(leftOp: Expr<*>, rightOp: Expr<*>): AddExpr<*> { - return if (leftOp is AddExpr<*>) { - val ops: List> = ImmutableList.builder>().addAll(leftOp.ops) - .add(rightOp) - .build() - AbstractExprs.Add(ops) - } else { - AbstractExprs.Add(leftOp, rightOp) - } - } + //// + override fun visitMultiplicativeExpr(ctx: MultiplicativeExprContext): Expr { + return if (ctx.ops.size >= 1) { + val opStream: Stream> = + ctx.ops.stream().map(Function { op: ExprContext -> op.accept>(this) }) + val ops = opStream.collect(Collectors.toList()) + val opsHead = ops[0] + val opsTail = ops.subList(1, ops.size) + createMutliplicativeExpr(opsHead, opsTail, ctx.oper, ctx) + } else { + visitChildren(ctx) + } + } - private fun createSubExpr(leftOp: Expr<*>, rightOp: Expr<*>): SubExpr<*> { - return AbstractExprs.Sub(leftOp, rightOp) - } + private fun createMutliplicativeExpr( + opsHead: Expr<*>, + opsTail: List>, + oper: Token, + ctx: MultiplicativeExprContext, + ): Expr { + return if (opsTail.isEmpty()) { + opsHead + } else { + val newOpsHead = opsTail[0] + val newOpsTail = opsTail.subList(1, opsTail.size) + val subExpr = createMultiplicativeSubExpr(opsHead, newOpsHead, oper, ctx) + createMutliplicativeExpr(subExpr, newOpsTail, oper, ctx) + } + } - private fun createBvAddExpr(leftOp: Expr, rightOp: Expr): BvAddExpr { - return if (leftOp is BvAddExpr) { - val ops: List> = ImmutableList.builder>() - .addAll(leftOp.ops).add(rightOp) - .build() - BvExprs.Add(ops) - } else { - BvExprs.Add(Arrays.asList(leftOp, rightOp)) - } - } + private fun createMultiplicativeSubExpr( + leftOp: Expr<*>, + rightOp: Expr<*>, + oper: Token, + ctx: MultiplicativeExprContext, + ): Expr { + return when (oper.type) { + MUL -> createMulExpr(leftOp, rightOp) + BV_MUL -> createBvMulExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + DIV -> createDivExpr(leftOp, rightOp) + BV_UDIV -> createBvUDivExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SDIV -> createBvSDivExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + MOD -> createModExpr(leftOp, rightOp) + BV_SMOD -> createBvSModExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + REM -> createRemExpr(leftOp, rightOp) + BV_UREM -> createBvURemExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + BV_SREM -> createBvSRemExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + FPREM -> FpExprs.Rem(leftOp as Expr, rightOp as Expr) + FPMUL -> + FpExprs.Mul( + getRoundingMode(ctx.oper.text), + java.util.List.of(leftOp as Expr, rightOp as Expr), + ) + + FPDIV -> + FpExprs.Div( + getRoundingMode(ctx.oper.text), + leftOp as Expr, + rightOp as Expr, + ) + + else -> throw ParseException(ctx, "Unknown operator '" + oper.text + "'") + } + } - private fun createBvSubExpr(leftOp: Expr, rightOp: Expr): BvSubExpr { - return BvExprs.Sub(leftOp, rightOp) - } + private fun createMulExpr(leftOp: Expr<*>, rightOp: Expr<*>): MulExpr<*> { + return if (leftOp is MulExpr<*>) { + val ops: List> = + ImmutableList.builder>().addAll(leftOp.ops).add(rightOp).build() + AbstractExprs.Mul(ops) + } else { + AbstractExprs.Mul(leftOp, rightOp) + } + } - //// - override fun visitMultiplicativeExpr(ctx: MultiplicativeExprContext): Expr { - return if (ctx.ops.size >= 1) { - val opStream: Stream> = ctx.ops.stream() - .map(Function { op: ExprContext -> op.accept>(this) }) - val ops = opStream.collect(Collectors.toList()) - val opsHead = ops[0] - val opsTail = ops.subList(1, ops.size) - createMutliplicativeExpr(opsHead, opsTail, ctx.oper, ctx) - } else { - visitChildren(ctx) - } - } + private fun createBvMulExpr(leftOp: Expr, rightOp: Expr): BvMulExpr { + return if (leftOp is BvMulExpr) { + val ops: List> = + ImmutableList.builder>().addAll(leftOp.ops).add(rightOp).build() + BvExprs.Mul(ops) + } else { + BvExprs.Mul(Arrays.asList(leftOp, rightOp)) + } + } - private fun createMutliplicativeExpr(opsHead: Expr<*>, opsTail: List>, - oper: Token, ctx: MultiplicativeExprContext): Expr { - return if (opsTail.isEmpty()) { - opsHead - } else { - val newOpsHead = opsTail[0] - val newOpsTail = opsTail.subList(1, opsTail.size) - val subExpr = createMultiplicativeSubExpr(opsHead, newOpsHead, oper, ctx) - createMutliplicativeExpr(subExpr, newOpsTail, oper, ctx) - } - } + private fun createDivExpr(leftOp: Expr<*>, rightOp: Expr<*>): DivExpr<*> { + return AbstractExprs.Div(leftOp, rightOp) + } - private fun createMultiplicativeSubExpr(leftOp: Expr<*>, rightOp: Expr<*>, oper: Token, - ctx: MultiplicativeExprContext): Expr { - return when (oper.type) { - MUL -> createMulExpr(leftOp, rightOp) - BV_MUL -> createBvMulExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - DIV -> createDivExpr(leftOp, rightOp) - BV_UDIV -> createBvUDivExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SDIV -> createBvSDivExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - MOD -> createModExpr(leftOp, rightOp) - BV_SMOD -> createBvSModExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - REM -> createRemExpr(leftOp, rightOp) - BV_UREM -> createBvURemExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - BV_SREM -> createBvSRemExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - FPREM -> FpExprs.Rem(leftOp as Expr, rightOp as Expr) - FPMUL -> FpExprs.Mul(getRoundingMode(ctx.oper.text), - java.util.List.of(leftOp as Expr, rightOp as Expr)) - - FPDIV -> FpExprs.Div(getRoundingMode(ctx.oper.text), leftOp as Expr, - rightOp as Expr) - - else -> throw ParseException(ctx, "Unknown operator '" + oper.text + "'") - } - } + private fun createBvUDivExpr(leftOp: Expr, rightOp: Expr): BvUDivExpr { + return BvExprs.UDiv(leftOp, rightOp) + } - private fun createMulExpr(leftOp: Expr<*>, rightOp: Expr<*>): MulExpr<*> { - return if (leftOp is MulExpr<*>) { - val ops: List> = ImmutableList.builder>().addAll(leftOp.ops) - .add(rightOp) - .build() - AbstractExprs.Mul(ops) - } else { - AbstractExprs.Mul(leftOp, rightOp) - } - } + private fun createBvSDivExpr(leftOp: Expr, rightOp: Expr): BvSDivExpr { + return BvExprs.SDiv(leftOp, rightOp) + } - private fun createBvMulExpr(leftOp: Expr, rightOp: Expr): BvMulExpr { - return if (leftOp is BvMulExpr) { - val ops: List> = ImmutableList.builder>() - .addAll(leftOp.ops).add(rightOp) - .build() - BvExprs.Mul(ops) - } else { - BvExprs.Mul(Arrays.asList(leftOp, rightOp)) - } - } + private fun createModExpr(uncastLeftOp: Expr<*>, uncastRightOp: Expr<*>): ModExpr<*> { + return AbstractExprs.Mod(uncastLeftOp, uncastRightOp) + } - private fun createDivExpr(leftOp: Expr<*>, rightOp: Expr<*>): DivExpr<*> { - return AbstractExprs.Div(leftOp, rightOp) - } + private fun createBvSModExpr(leftOp: Expr, rightOp: Expr): BvSModExpr { + return BvExprs.SMod(leftOp, rightOp) + } - private fun createBvUDivExpr(leftOp: Expr, rightOp: Expr): BvUDivExpr { - return BvExprs.UDiv(leftOp, rightOp) - } + private fun createRemExpr(uncastLeftOp: Expr<*>, uncastRightOp: Expr<*>): RemExpr<*> { + return AbstractExprs.Rem(uncastLeftOp, uncastRightOp) + } - private fun createBvSDivExpr(leftOp: Expr, rightOp: Expr): BvSDivExpr { - return BvExprs.SDiv(leftOp, rightOp) - } + private fun createBvURemExpr(leftOp: Expr, rightOp: Expr): BvURemExpr { + return BvExprs.URem(leftOp, rightOp) + } - private fun createModExpr(uncastLeftOp: Expr<*>, uncastRightOp: Expr<*>): ModExpr<*> { - return AbstractExprs.Mod(uncastLeftOp, uncastRightOp) - } + private fun createBvSRemExpr(leftOp: Expr, rightOp: Expr): BvSRemExpr { + return BvExprs.SRem(leftOp, rightOp) + } - private fun createBvSModExpr(leftOp: Expr, rightOp: Expr): BvSModExpr { - return BvExprs.SMod(leftOp, rightOp) - } + //// + override fun visitBvConcatExpr(ctx: BvConcatExprContext): Expr { + return if (ctx.ops.size >= 1) { + val opStream: Stream> = + ctx.ops.stream().map(Function { op: ExprContext -> op.accept>(this) }) + val ops = opStream.collect(Collectors.toList()) + val opsHead = ops[0] + val opsTail = ops.subList(1, ops.size) + createConcatExpr(opsHead, opsTail, ctx.oper) + } else { + visitChildren(ctx) + } + } - private fun createRemExpr(uncastLeftOp: Expr<*>, uncastRightOp: Expr<*>): RemExpr<*> { - return AbstractExprs.Rem(uncastLeftOp, uncastRightOp) - } + private fun createConcatExpr( + opsHead: Expr<*>, + opsTail: List>, + oper: Token, + ): Expr { + return if (opsTail.isEmpty()) { + opsHead + } else { + val newOpsHead = opsTail[0] + val newOpsTail = opsTail.subList(1, opsTail.size) + val subExpr = createConcatSubExpr(opsHead, newOpsHead, oper) + createConcatExpr(subExpr, newOpsTail, oper) + } + } - private fun createBvURemExpr(leftOp: Expr, rightOp: Expr): BvURemExpr { - return BvExprs.URem(leftOp, rightOp) - } + private fun createConcatSubExpr( + leftOp: Expr<*>, + rightOp: Expr<*>, + oper: Token, + ): Expr { + return when (oper.type) { + BV_CONCAT -> createBvConcatExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) + else -> throw AssertionError() + } + } - private fun createBvSRemExpr(leftOp: Expr, rightOp: Expr): BvSRemExpr { - return BvExprs.SRem(leftOp, rightOp) - } + private fun createBvConcatExpr(leftOp: Expr, rightOp: Expr): BvConcatExpr { + return if (leftOp is BvConcatExpr) { + val ops: List> = + ImmutableList.builder>().addAll(leftOp.ops).add(rightOp).build() + BvExprs.Concat(ops) + } else { + BvExprs.Concat(Arrays.asList(leftOp, rightOp)) + } + } - //// - override fun visitBvConcatExpr(ctx: BvConcatExprContext): Expr { - return if (ctx.ops.size >= 1) { - val opStream: Stream> = ctx.ops.stream() - .map(Function { op: ExprContext -> op.accept>(this) }) - val ops = opStream.collect(Collectors.toList()) - val opsHead = ops[0] - val opsTail = ops.subList(1, ops.size) - createConcatExpr(opsHead, opsTail, ctx.oper) - } else { - visitChildren(ctx) - } - } + override fun visitBvExtendExpr(ctx: BvExtendExprContext): Expr { + return if (ctx.rightOp != null) { + val extendType = BvExprs.BvType(ctx.rightOp.size.getText().toInt()) + when (ctx.oper.getType()) { + BV_ZERO_EXTEND -> + BvExprs.ZExt(TypeUtils.castBv(ctx.leftOp.accept>(this)), extendType) - private fun createConcatExpr(opsHead: Expr<*>, opsTail: List>, - oper: Token): Expr { - return if (opsTail.isEmpty()) { - opsHead - } else { - val newOpsHead = opsTail[0] - val newOpsTail = opsTail.subList(1, opsTail.size) - val subExpr = createConcatSubExpr(opsHead, newOpsHead, oper) - createConcatExpr(subExpr, newOpsTail, oper) - } - } + BV_SIGN_EXTEND -> + BvExprs.SExt(TypeUtils.castBv(ctx.leftOp.accept>(this)), extendType) - private fun createConcatSubExpr(leftOp: Expr<*>, rightOp: Expr<*>, - oper: Token): Expr { - return when (oper.type) { - BV_CONCAT -> createBvConcatExpr(TypeUtils.castBv(leftOp), TypeUtils.castBv(rightOp)) - else -> throw AssertionError() - } + else -> throw AssertionError() } + } else { + visitChildren(ctx) + } + } - private fun createBvConcatExpr(leftOp: Expr, rightOp: Expr): BvConcatExpr { - return if (leftOp is BvConcatExpr) { - val ops: List> = ImmutableList.builder>() - .addAll(leftOp.ops).add(rightOp) - .build() - BvExprs.Concat(ops) - } else { - BvExprs.Concat(Arrays.asList(leftOp, rightOp)) - } - } + //// + override fun visitUnaryExpr(ctx: UnaryExprContext): Expr { + return if (ctx.op != null) { + val op: Expr<*> = ctx.op.accept>(this) + when (ctx.oper.getType()) { + PLUS -> AbstractExprs.Pos(op) + MINUS -> AbstractExprs.Neg(op) + FP_ABS -> FpExprs.Abs(op as Expr) + FP_IS_INF -> FpExprs.IsInfinite(op as Expr) + FP_IS_NAN -> FpExprs.IsNan(op as Expr) + FPROUNDTOINT -> + FpExprs.RoundToIntegral(getRoundingMode(ctx.oper.text), op as Expr) + + FPSQRT -> FpExprs.Sqrt(getRoundingMode(ctx.oper.text), op as Expr) + FPTOFP -> + FpExprs.ToFp( + getRoundingMode(ctx.oper.text), + op as Expr, + getExp(ctx.oper.getText()), + getSignificand(ctx.oper.getText()), + ) - override fun visitBvExtendExpr(ctx: BvExtendExprContext): Expr { - return if (ctx.rightOp != null) { - val extendType = BvExprs.BvType(ctx.rightOp.size.getText().toInt()) - when (ctx.oper.getType()) { - BV_ZERO_EXTEND -> BvExprs.ZExt( - TypeUtils.castBv(ctx.leftOp.accept>(this)), extendType) - - BV_SIGN_EXTEND -> BvExprs.SExt( - TypeUtils.castBv(ctx.leftOp.accept>(this)), extendType) - - else -> throw AssertionError() - } - } else { - visitChildren(ctx) - } - } + FPTOBV -> + FpExprs.ToBv( + getRoundingMode(ctx.oper.text), + op as Expr, + getBvSize(ctx.oper.getText()), + isSignedBv(ctx.oper.getText()), + ) - //// - override fun visitUnaryExpr(ctx: UnaryExprContext): Expr { - return if (ctx.op != null) { - val op: Expr<*> = ctx.op.accept>(this) - when (ctx.oper.getType()) { - PLUS -> AbstractExprs.Pos(op) - MINUS -> AbstractExprs.Neg(op) - FP_ABS -> FpExprs.Abs(op as Expr) - FP_IS_INF -> FpExprs.IsInfinite(op as Expr) - FP_IS_NAN -> FpExprs.IsNan(op as Expr) - FPROUNDTOINT -> FpExprs.RoundToIntegral(getRoundingMode(ctx.oper.text), - op as Expr) - - FPSQRT -> FpExprs.Sqrt(getRoundingMode(ctx.oper.text), op as Expr) - FPTOFP -> FpExprs.ToFp(getRoundingMode(ctx.oper.text), op as Expr, - getExp(ctx.oper.getText()), getSignificand(ctx.oper.getText())) - - FPTOBV -> FpExprs.ToBv(getRoundingMode(ctx.oper.text), op as Expr, - getBvSize(ctx.oper.getText()), isSignedBv(ctx.oper.getText())) - - FP_FROM_BV -> FpExprs.FromBv(getRoundingMode(ctx.oper.text), - op as Expr, - FpType.of(getExp(ctx.oper.getText()), getSignificand(ctx.oper.getText())), - isSignedFp(ctx.oper.getText())) - - FPNEG -> FpExprs.Neg(op as Expr) - FPPOS -> FpExprs.Pos(op as Expr) - BV_POS -> BvExprs.Pos(op as Expr) - BV_NEG -> BvExprs.Neg(op as Expr) - else -> throw ParseException(ctx, "Unknown operator") - } - } else { - visitChildren(ctx) - } - } + FP_FROM_BV -> + FpExprs.FromBv( + getRoundingMode(ctx.oper.text), + op as Expr, + FpType.of(getExp(ctx.oper.getText()), getSignificand(ctx.oper.getText())), + isSignedFp(ctx.oper.getText()), + ) - private fun isSignedFp(text: String): Boolean { - val pattern = Pattern.compile("\\[([us])]") - val matcher = pattern.matcher(text) - return if (matcher.find()) { - matcher.group(1) != "u" - } else false + FPNEG -> FpExprs.Neg(op as Expr) + FPPOS -> FpExprs.Pos(op as Expr) + BV_POS -> BvExprs.Pos(op as Expr) + BV_NEG -> BvExprs.Neg(op as Expr) + else -> throw ParseException(ctx, "Unknown operator") } + } else { + visitChildren(ctx) + } + } - private fun isSignedBv(text: String): Boolean { - val pattern = Pattern.compile("\\[[0-9]*'([us])]") - val matcher = pattern.matcher(text) - return if (matcher.find()) { - matcher.group(1) != "u" - } else throw RuntimeException("Signedness not well formed in bv type!") - } + private fun isSignedFp(text: String): Boolean { + val pattern = Pattern.compile("\\[([us])]") + val matcher = pattern.matcher(text) + return if (matcher.find()) { + matcher.group(1) != "u" + } else false + } - private fun getBvSize(text: String): Int { - val pattern = Pattern.compile("\\[([0-9]*)'[us]]") - val matcher = pattern.matcher(text) - return if (matcher.find()) { - matcher.group(1).toInt() - } else throw RuntimeException("Size not well formed in bv type!") - } + private fun isSignedBv(text: String): Boolean { + val pattern = Pattern.compile("\\[[0-9]*'([us])]") + val matcher = pattern.matcher(text) + return if (matcher.find()) { + matcher.group(1) != "u" + } else throw RuntimeException("Signedness not well formed in bv type!") + } - private fun getExp(text: String): Int { - val pattern = Pattern.compile("\\[([0-9]*),([0-9]*)]") - val matcher = pattern.matcher(text) - return if (matcher.find()) { - matcher.group(1).toInt() - } else throw RuntimeException("Exponent not well formed in fp type!") - } + private fun getBvSize(text: String): Int { + val pattern = Pattern.compile("\\[([0-9]*)'[us]]") + val matcher = pattern.matcher(text) + return if (matcher.find()) { + matcher.group(1).toInt() + } else throw RuntimeException("Size not well formed in bv type!") + } - private fun getSignificand(text: String): Int { - val pattern = Pattern.compile("\\[([0-9]*),([0-9]*)]") - val matcher = pattern.matcher(text) - return if (matcher.find()) { - matcher.group(2).toInt() - } else throw RuntimeException("Significand not well formed in fp type!") - } + private fun getExp(text: String): Int { + val pattern = Pattern.compile("\\[([0-9]*),([0-9]*)]") + val matcher = pattern.matcher(text) + return if (matcher.find()) { + matcher.group(1).toInt() + } else throw RuntimeException("Exponent not well formed in fp type!") + } - private fun getRoundingMode(text: String): FpRoundingMode { - val pattern = Pattern.compile("\\[((RNA)|(RNE)|(RTP)|(RTN)|(RTZ))]") - val matcher = pattern.matcher(text) - if (matcher.find()) { - return FpRoundingMode.valueOf(matcher.group(1).uppercase()) - } - return FpRoundingMode.getDefaultRoundingMode() - } + private fun getSignificand(text: String): Int { + val pattern = Pattern.compile("\\[([0-9]*),([0-9]*)]") + val matcher = pattern.matcher(text) + return if (matcher.find()) { + matcher.group(2).toInt() + } else throw RuntimeException("Significand not well formed in fp type!") + } - override fun visitBitwiseNotExpr(ctx: BitwiseNotExprContext): Expr { - return if (ctx.op != null) { - val op = TypeUtils.castBv(ctx.op.accept>(this)) - BvExprs.Not(op) - } else { - visitChildren(ctx) - } - } + private fun getRoundingMode(text: String): FpRoundingMode { + val pattern = Pattern.compile("\\[((RNA)|(RNE)|(RTP)|(RTN)|(RTZ))]") + val matcher = pattern.matcher(text) + if (matcher.find()) { + return FpRoundingMode.valueOf(matcher.group(1).uppercase()) + } + return FpRoundingMode.getDefaultRoundingMode() + } - override fun visitFunctionCall(ctx: FunctionCallContext): Expr { - return if (ctx.op != null) { - throw UnsupportedOperationException("Function application not yet supported.") - } else { - visitChildren(ctx) - } - } + override fun visitBitwiseNotExpr(ctx: BitwiseNotExprContext): Expr { + return if (ctx.op != null) { + val op = TypeUtils.castBv(ctx.op.accept>(this)) + BvExprs.Not(op) + } else { + visitChildren(ctx) + } + } - override fun visitArrayRead(ctx: ArrayReadContext): Expr { - return if (ctx.array != null) { - ArrayReadExpr.create( - ctx.array.accept(this), - ctx.index.accept(this)) - } else { - visitChildren(ctx) - } - } + override fun visitFunctionCall(ctx: FunctionCallContext): Expr { + return if (ctx.op != null) { + throw UnsupportedOperationException("Function application not yet supported.") + } else { + visitChildren(ctx) + } + } - override fun visitArrayWrite(ctx: ArrayWriteContext): Expr { - return if (ctx.array != null) { - ArrayWriteExpr.create( - ctx.array.accept(this), - ctx.index.accept(this), - ctx.elem.accept(this)) - } else { - visitChildren(ctx) - } - } + override fun visitArrayRead(ctx: ArrayReadContext): Expr { + return if (ctx.array != null) { + ArrayReadExpr.create(ctx.array.accept(this), ctx.index.accept(this)) + } else { + visitChildren(ctx) + } + } - override fun visitPrimeExpr(ctx: PrimeExprContext): Expr { - return if (ctx.op != null) { - Exprs.Prime(ctx.op.accept(this)) - } else { - visitChildren(ctx) - } - } + override fun visitArrayWrite(ctx: ArrayWriteContext): Expr { + return if (ctx.array != null) { + ArrayWriteExpr.create( + ctx.array.accept(this), + ctx.index.accept(this), + ctx.elem.accept(this), + ) + } else { + visitChildren(ctx) + } + } - override fun visitBvExtract(ctx: BvExtractContext): Expr { - return if (ctx.op != null) { - val op = ctx.op.accept(this) - val bitvec = TypeUtils.castBv(op) - return BvExprs.Extract(bitvec, Int(ctx.from.getText()), - IntExprs.Int(ctx.until.getText())) - } else { - visitChildren(ctx) - } - } + override fun visitPrimeExpr(ctx: PrimeExprContext): Expr { + return if (ctx.op != null) { + Exprs.Prime(ctx.op.accept(this)) + } else { + visitChildren(ctx) + } + } - override fun visitDerefExpr(ctx: DerefExprContext): Expr { - return if (ctx.base != null) { - val base = ctx.base.accept(this) - val offset = ctx.offset.accept(this) - val type = TypeWrapper(ctx.type().textWithWS()).instantiate() - return Dereference(cast(base, base.type), cast(offset, base.type), type); - } else { - visitChildren(ctx) - } - } + override fun visitBvExtract(ctx: BvExtractContext): Expr { + return if (ctx.op != null) { + val op = ctx.op.accept(this) + val bitvec = TypeUtils.castBv(op) + return BvExprs.Extract(bitvec, Int(ctx.from.getText()), IntExprs.Int(ctx.until.getText())) + } else { + visitChildren(ctx) + } + } - override fun visitRefExpr(ctx: RefExprContext): Expr { - return if (ctx.expr() != null) { - val expr = ctx.expr().accept(this) - val type = TypeWrapper(ctx.type().textWithWS()).instantiate() - return Reference(expr, type) - } else { - visitChildren(ctx) - } - } + override fun visitDerefExpr(ctx: DerefExprContext): Expr { + return if (ctx.base != null) { + val base = ctx.base.accept(this) + val offset = ctx.offset.accept(this) + val type = TypeWrapper(ctx.type().textWithWS()).instantiate() + return Dereference(cast(base, base.type), cast(offset, base.type), type) + } else { + visitChildren(ctx) + } + } - //// - override fun visitTrueExpr(ctx: TrueExprContext): TrueExpr { - return BoolExprs.True() - } + override fun visitRefExpr(ctx: RefExprContext): Expr { + return if (ctx.expr() != null) { + val expr = ctx.expr().accept(this) + val type = TypeWrapper(ctx.type().textWithWS()).instantiate() + return Reference(expr, type) + } else { + visitChildren(ctx) + } + } - override fun visitFalseExpr(ctx: FalseExprContext): FalseExpr { - return BoolExprs.False() - } + //// + override fun visitTrueExpr(ctx: TrueExprContext): TrueExpr { + return BoolExprs.True() + } - override fun visitIntLitExpr(ctx: IntLitExprContext): IntLitExpr { - val value: BigInteger = BigInteger(ctx.text) - return IntExprs.Int(value) - } + override fun visitFalseExpr(ctx: FalseExprContext): FalseExpr { + return BoolExprs.False() + } - override fun visitRatLitExpr(ctx: RatLitExprContext): RatLitExpr { - val num = BigInteger(ctx.num.text) - val denom = BigInteger(ctx.denom.getText()) - return RatExprs.Rat(num, denom) - } + override fun visitIntLitExpr(ctx: IntLitExprContext): IntLitExpr { + val value: BigInteger = BigInteger(ctx.text) + return IntExprs.Int(value) + } - override fun visitFpLitExpr(ctx: FpLitExprContext): Expr { - val hidden = ctx.bvLitExpr(0).accept>(this) as BvLitExpr - val exponent = ctx.bvLitExpr(1).accept>(this) as BvLitExpr - val significand = ctx.bvLitExpr(2).accept>(this) as BvLitExpr - return FpLitExpr.of(hidden == Bv(BooleanArray(1) { true }), exponent, significand) - } + override fun visitRatLitExpr(ctx: RatLitExprContext): RatLitExpr { + val num = BigInteger(ctx.num.text) + val denom = BigInteger(ctx.denom.getText()) + return RatExprs.Rat(num, denom) + } - override fun visitArrLitExpr(ctx: ArrLitExprContext): Expr { - Preconditions.checkNotNull(ctx.elseExpr) - val indexType = if (ctx.indexExpr.size > 0) ctx.indexExpr[0].accept( - this).type else Int() - val elseElem = ctx.elseExpr.accept(this) - val valueType = elseElem.type - val elems = ctx.indexExpr.mapIndexed { idx, it -> - Tuple2.of(it.accept(this), ctx.valueExpr[idx].accept(this)) - } - return ExprUtils.simplify(ArrayInitExpr.create(elems, elseElem, - ArrayType.of(indexType, valueType))) - } + override fun visitFpLitExpr(ctx: FpLitExprContext): Expr { + val hidden = ctx.bvLitExpr(0).accept>(this) as BvLitExpr + val exponent = ctx.bvLitExpr(1).accept>(this) as BvLitExpr + val significand = ctx.bvLitExpr(2).accept>(this) as BvLitExpr + return FpLitExpr.of(hidden == Bv(BooleanArray(1) { true }), exponent, significand) + } - override fun visitBvLitExpr(ctx: BvLitExprContext): Expr { - val sizeAndContent: Array = ctx.bv.getText().split("['#]".toRegex()) - .dropLastWhile { it.isEmpty() }.toTypedArray() - val content = if (sizeAndContent.size == 2) sizeAndContent[1] else sizeAndContent[0] - val value: BooleanArray = if (content.startsWith("b")) { - decodeBinaryBvContent(content.substring(1)) - } else if (content.startsWith("d")) { - check( - sizeAndContent.size == 2) { "Decimal value is only parseable if size is given." } - decodeDecimalBvContent(content.substring(1), sizeAndContent[0].toInt()) - } else if (content.startsWith("x")) { - decodeHexadecimalBvContent(content.substring(1)) - } else { - throw ParseException(ctx, "Invalid bitvector literal") - } - val bvValue = BooleanArray(value.size) - for (i in value.indices) { - bvValue[value.size - 1 - i] = value[value.size - 1 - i] - } - return BvExprs.Bv(bvValue) - } + override fun visitArrLitExpr(ctx: ArrLitExprContext): Expr { + Preconditions.checkNotNull(ctx.elseExpr) + val indexType = if (ctx.indexExpr.size > 0) ctx.indexExpr[0].accept(this).type else Int() + val elseElem = ctx.elseExpr.accept(this) + val valueType = elseElem.type + val elems = + ctx.indexExpr.mapIndexed { idx, it -> + Tuple2.of(it.accept(this), ctx.valueExpr[idx].accept(this)) + } + return ExprUtils.simplify( + ArrayInitExpr.create(elems, elseElem, ArrayType.of(indexType, valueType)) + ) + } - private fun decodeBinaryBvContent(lit: String): BooleanArray { - val value = BooleanArray(lit.length) - for (i in 0 until lit.length) { - when (lit.toCharArray()[i]) { - '0' -> value[i] = false - '1' -> value[i] = true - else -> throw IllegalArgumentException( - "Binary literal can contain only 0 and 1") - } - } - return value - } + override fun visitBvLitExpr(ctx: BvLitExprContext): Expr { + val sizeAndContent: Array = + ctx.bv.getText().split("['#]".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + val content = if (sizeAndContent.size == 2) sizeAndContent[1] else sizeAndContent[0] + val value: BooleanArray = + if (content.startsWith("b")) { + decodeBinaryBvContent(content.substring(1)) + } else if (content.startsWith("d")) { + check(sizeAndContent.size == 2) { "Decimal value is only parseable if size is given." } + decodeDecimalBvContent(content.substring(1), sizeAndContent[0].toInt()) + } else if (content.startsWith("x")) { + decodeHexadecimalBvContent(content.substring(1)) + } else { + throw ParseException(ctx, "Invalid bitvector literal") + } + val bvValue = BooleanArray(value.size) + for (i in value.indices) { + bvValue[value.size - 1 - i] = value[value.size - 1 - i] + } + return BvExprs.Bv(bvValue) + } - private fun decodeDecimalBvContent(lit: String, size: Int): BooleanArray { - var value = BigInteger(lit) - Preconditions.checkArgument( - value.compareTo( - BigInteger.TWO.pow(size - 1).multiply(BigInteger.valueOf(-1))) >= 0 && - value.compareTo(BigInteger.TWO.pow(size)) < 0, - "Decimal literal is not in range" - ) - if (value.compareTo(BigInteger.ZERO) < 0) { - value = value.add(BigInteger.TWO.pow(size)) - } - return decodeBinaryBvContent(value.toString(2)) + private fun decodeBinaryBvContent(lit: String): BooleanArray { + val value = BooleanArray(lit.length) + for (i in 0 until lit.length) { + when (lit.toCharArray()[i]) { + '0' -> value[i] = false + '1' -> value[i] = true + else -> throw IllegalArgumentException("Binary literal can contain only 0 and 1") } + } + return value + } - private fun decodeHexadecimalBvContent(lit: String): BooleanArray { - val builder = StringBuilder() - for (i in 0 until lit.length) { - when (lit.toCharArray()[i].lowercaseChar()) { - '0' -> builder.append("0000") - '1' -> builder.append("0001") - '2' -> builder.append("0010") - '3' -> builder.append("0011") - '4' -> builder.append("0100") - '5' -> builder.append("0101") - '6' -> builder.append("0110") - '7' -> builder.append("0111") - '8' -> builder.append("1000") - '9' -> builder.append("1001") - 'a' -> builder.append("1010") - 'b' -> builder.append("1011") - 'c' -> builder.append("1100") - 'd' -> builder.append("1101") - 'e' -> builder.append("1110") - 'f' -> builder.append("1111") - else -> throw IllegalArgumentException("Invalid hexadecimal character") - } - } - return decodeBinaryBvContent(builder.toString()) - } + private fun decodeDecimalBvContent(lit: String, size: Int): BooleanArray { + var value = BigInteger(lit) + Preconditions.checkArgument( + value.compareTo(BigInteger.TWO.pow(size - 1).multiply(BigInteger.valueOf(-1))) >= 0 && + value.compareTo(BigInteger.TWO.pow(size)) < 0, + "Decimal literal is not in range", + ) + if (value.compareTo(BigInteger.ZERO) < 0) { + value = value.add(BigInteger.TWO.pow(size)) + } + return decodeBinaryBvContent(value.toString(2)) + } - override fun visitIdExpr(ctx: IdExprContext): RefExpr<*> { - val optSymbol = currentScope.resolve(ctx.id.getText()) - if (optSymbol.isEmpty) { - throw ParseException(ctx, - "Identifier '" + ctx.id.getText() + "' cannot be resolved") - } - val symbol = optSymbol.get() - val decl = env.eval(symbol) as Decl<*> - return decl.ref - } + private fun decodeHexadecimalBvContent(lit: String): BooleanArray { + val builder = StringBuilder() + for (i in 0 until lit.length) { + when (lit.toCharArray()[i].lowercaseChar()) { + '0' -> builder.append("0000") + '1' -> builder.append("0001") + '2' -> builder.append("0010") + '3' -> builder.append("0011") + '4' -> builder.append("0100") + '5' -> builder.append("0101") + '6' -> builder.append("0110") + '7' -> builder.append("0111") + '8' -> builder.append("1000") + '9' -> builder.append("1001") + 'a' -> builder.append("1010") + 'b' -> builder.append("1011") + 'c' -> builder.append("1100") + 'd' -> builder.append("1101") + 'e' -> builder.append("1110") + 'f' -> builder.append("1111") + else -> throw IllegalArgumentException("Invalid hexadecimal character") + } + } + return decodeBinaryBvContent(builder.toString()) + } - override fun visitParenExpr(ctx: ParenExprContext): Expr { - return ctx.op.accept>(this) - } + override fun visitIdExpr(ctx: IdExprContext): RefExpr<*> { + val optSymbol = currentScope.resolve(ctx.id.getText()) + if (optSymbol.isEmpty) { + throw ParseException(ctx, "Identifier '" + ctx.id.getText() + "' cannot be resolved") + } + val symbol = optSymbol.get() + val decl = env.eval(symbol) as Decl<*> + return decl.ref + } + + override fun visitParenExpr(ctx: ParenExprContext): Expr { + return ctx.op.accept>(this) } -} \ No newline at end of file + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/stmt/StmtParser.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/stmt/StmtParser.kt index 09ea6e8f9d..00bf2ad1b9 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/stmt/StmtParser.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/dsl/stmt/StmtParser.kt @@ -42,80 +42,81 @@ import org.antlr.v4.runtime.CommonTokenStream class StatementWrapper(val content: String, scope: Scope) { - private val scope: Scope - private val context: StmtContext + private val scope: Scope + private val context: StmtContext - init { - this.scope = Preconditions.checkNotNull(scope) - val lexer = StmtLexer(CharStreams.fromString(content)) - lexer.addErrorListener(ThrowingErrorListener) - val parser = StmtParser(CommonTokenStream(lexer)) - parser.errorHandler = BailErrorStrategy() - this.context = Preconditions.checkNotNull(parser.stmt()) - } + init { + this.scope = Preconditions.checkNotNull(scope) + val lexer = StmtLexer(CharStreams.fromString(content)) + lexer.addErrorListener(ThrowingErrorListener) + val parser = StmtParser(CommonTokenStream(lexer)) + parser.errorHandler = BailErrorStrategy() + this.context = Preconditions.checkNotNull(parser.stmt()) + } - fun instantiate(env: Env?): Stmt { - val visitor = StmtCreatorVisitor(scope, env) - return try { - context.accept(visitor) - } catch (e: Exception) { - System.err.println("Erroneous input: ${context.textWithWS()}") - throw e - } + fun instantiate(env: Env?): Stmt { + val visitor = StmtCreatorVisitor(scope, env) + return try { + context.accept(visitor) + } catch (e: Exception) { + System.err.println("Erroneous input: ${context.textWithWS()}") + throw e } + } - private class StmtCreatorVisitor(scope: Scope?, env: Env?) : StmtBaseVisitor() { + private class StmtCreatorVisitor(scope: Scope?, env: Env?) : StmtBaseVisitor() { - private val scope: Scope - private val env: Env + private val scope: Scope + private val env: Env - init { - this.scope = scope!! // TODO replaced a checkNotNull with !! here - this.env = env!! // TODO replaced a checkNotNull with !! here - } + init { + this.scope = scope!! // TODO replaced a checkNotNull with !! here + this.env = env!! // TODO replaced a checkNotNull with !! here + } - override fun visitSkipStmt(ctx: SkipStmtContext): Stmt { - return SkipStmt.getInstance() - } + override fun visitSkipStmt(ctx: SkipStmtContext): Stmt { + return SkipStmt.getInstance() + } - override fun visitHavocStmt(ctx: HavocStmtContext): Stmt { - val lhsId: String = ctx.lhs.getText() - val lhsSymbol = scope.resolve(lhsId).get() - val `var` = env.eval(lhsSymbol) as VarDecl<*> - return Stmts.Havoc(`var`) - } + override fun visitHavocStmt(ctx: HavocStmtContext): Stmt { + val lhsId: String = ctx.lhs.getText() + val lhsSymbol = scope.resolve(lhsId).get() + val `var` = env.eval(lhsSymbol) as VarDecl<*> + return Stmts.Havoc(`var`) + } - override fun visitAssumeStmt(ctx: AssumeStmtContext): Stmt { - val expression = ExpressionWrapper(scope, ctx.cond.textWithWS()) - val expr: Expr = TypeUtils.cast(expression.instantiate(env), BoolExprs.Bool()) - return Stmts.Assume(expr) - } + override fun visitAssumeStmt(ctx: AssumeStmtContext): Stmt { + val expression = ExpressionWrapper(scope, ctx.cond.textWithWS()) + val expr: Expr = TypeUtils.cast(expression.instantiate(env), BoolExprs.Bool()) + return Stmts.Assume(expr) + } - override fun visitAssignStmt(ctx: AssignStmtContext): Stmt { - val lhsId: String = ctx.lhs.getText() - val lhsSymbol = scope.resolve(lhsId).get() - val `var` = env.eval(lhsSymbol) as VarDecl<*> - val expression = ExpressionWrapper(scope, ctx.value.textWithWS()) - val expr: Expr<*> = expression.instantiate(env) - return if (expr.type == `var`.type) { - val tVar = `var` as VarDecl - val tExpr = expr as Expr - Stmts.Assign(tVar, tExpr) - } else { - throw IllegalArgumentException("Type of $`var` is incompatible with $expr") - } - } + override fun visitAssignStmt(ctx: AssignStmtContext): Stmt { + val lhsId: String = ctx.lhs.getText() + val lhsSymbol = scope.resolve(lhsId).get() + val `var` = env.eval(lhsSymbol) as VarDecl<*> + val expression = ExpressionWrapper(scope, ctx.value.textWithWS()) + val expr: Expr<*> = expression.instantiate(env) + return if (expr.type == `var`.type) { + val tVar = `var` as VarDecl + val tExpr = expr as Expr + Stmts.Assign(tVar, tExpr) + } else { + throw IllegalArgumentException("Type of $`var` is incompatible with $expr") + } + } - override fun visitMemAssignStmt(ctx: MemAssignStmtContext): Stmt { - val derefExpr: Dereference<*, *, *> = ExpressionWrapper(scope, ctx.derefExpr().textWithWS()).instantiate( - env) as Dereference<*, *, *> - val value = ExpressionWrapper(scope, ctx.value.textWithWS()) - val valueE: Expr<*> = value.instantiate(env) - return if (derefExpr.type == valueE.type) { - MemoryAssignStmt.create(derefExpr, valueE) - } else { - throw IllegalArgumentException("Type of $derefExpr is incompatible with $valueE") - } - } + override fun visitMemAssignStmt(ctx: MemAssignStmtContext): Stmt { + val derefExpr: Dereference<*, *, *> = + ExpressionWrapper(scope, ctx.derefExpr().textWithWS()).instantiate(env) + as Dereference<*, *, *> + val value = ExpressionWrapper(scope, ctx.value.textWithWS()) + val valueE: Expr<*> = value.instantiate(env) + return if (derefExpr.type == valueE.type) { + MemoryAssignStmt.create(derefExpr, valueE) + } else { + throw IllegalArgumentException("Type of $derefExpr is incompatible with $valueE") + } } -} \ No newline at end of file + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapter.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapter.kt index a05598f87e..be2030d86e 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapter.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -26,28 +25,30 @@ import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.algorithm.arg.ARG import java.lang.reflect.Type -class ArgAdapter(val gsonSupplier: () -> Gson, - private val partialOrdSupplier: () -> PartialOrd, - private val argTypeSupplier: () -> Type) : TypeAdapter>() { +class ArgAdapter( + val gsonSupplier: () -> Gson, + private val partialOrdSupplier: () -> PartialOrd, + private val argTypeSupplier: () -> Type, +) : TypeAdapter>() { - private lateinit var gson: Gson - private lateinit var partialOrd: PartialOrd - private lateinit var argType: Type + private lateinit var gson: Gson + private lateinit var partialOrd: PartialOrd + private lateinit var argType: Type - override fun write(writer: JsonWriter, value: ARG<*, *>) { - initGson() - gson.toJson(gson.toJsonTree(ArgAdapterHelper(value)), writer) - } + override fun write(writer: JsonWriter, value: ARG<*, *>) { + initGson() + gson.toJson(gson.toJsonTree(ArgAdapterHelper(value)), writer) + } - override fun read(reader: JsonReader): ARG<*, *> { - initGson() - if (!this::partialOrd.isInitialized) partialOrd = partialOrdSupplier() as PartialOrd - if (!this::argType.isInitialized) argType = argTypeSupplier() - val argAdapterHelper: ArgAdapterHelper = gson.fromJson(reader, argType) - return argAdapterHelper.instantiate(partialOrd) - } + override fun read(reader: JsonReader): ARG<*, *> { + initGson() + if (!this::partialOrd.isInitialized) partialOrd = partialOrdSupplier() as PartialOrd + if (!this::argType.isInitialized) argType = argTypeSupplier() + val argAdapterHelper: ArgAdapterHelper = gson.fromJson(reader, argType) + return argAdapterHelper.instantiate(partialOrd) + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } -} \ No newline at end of file + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapterHelper.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapterHelper.kt index f00ff2ac89..575b3e9ca8 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapterHelper.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/ArgAdapterHelper.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import hu.bme.mit.theta.analysis.Action @@ -23,59 +22,70 @@ import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.algorithm.arg.ArgNode data class ArgAdapterHelper( - val initNodes: Map>, - val nodes: Map>, - val edges: Map>, - val coveringEdges: Map + val initNodes: Map>, + val nodes: Map>, + val edges: Map>, + val coveringEdges: Map, ) { - constructor(arg: ARG) : this( - initNodes = arg.initNodes.map { Pair(it.id, Triple(it.state, it.isTarget, it.isExpanded)) } - .toList().associate { it.first to it.second }, - nodes = arg.nodes.map { Pair(it.id, ArgNodeAdapter(it.isTarget, it.state, it.isExpanded)) } - .toList().associate { it.first to it.second }, - edges = arg.nodes.filter { it.inEdge.isPresent } - .map { Pair(it.id, ArgEdgeAdapter(it.inEdge.get().source.id, it.inEdge.get().action)) } - .toList().associate { it.first to it.second }, - coveringEdges = arg.nodes.filter { it.coveringNode.isPresent } - .map { Pair(it.id, it.coveringNode.get().id) }.toList() - .associate { it.first to it.second } - ) + constructor( + arg: ARG + ) : this( + initNodes = + arg.initNodes + .map { Pair(it.id, Triple(it.state, it.isTarget, it.isExpanded)) } + .toList() + .associate { it.first to it.second }, + nodes = + arg.nodes + .map { Pair(it.id, ArgNodeAdapter(it.isTarget, it.state, it.isExpanded)) } + .toList() + .associate { it.first to it.second }, + edges = + arg.nodes + .filter { it.inEdge.isPresent } + .map { Pair(it.id, ArgEdgeAdapter(it.inEdge.get().source.id, it.inEdge.get().action)) } + .toList() + .associate { it.first to it.second }, + coveringEdges = + arg.nodes + .filter { it.coveringNode.isPresent } + .map { Pair(it.id, it.coveringNode.get().id) } + .toList() + .associate { it.first to it.second }, + ) - fun instantiate(partialOrd: PartialOrd): ARG { - val arg = ARG.create(partialOrd) - val lut = HashMap>() - initNodes.forEach { - lut[it.key] = arg.createInitNode(it.value.first, it.value.second) - .also { n -> if (it.value.third) n.expanded = true } - } - arg.initialized = true - val waitSet = HashSet(edges.keys) - while (waitSet.isNotEmpty()) { - val entry = waitSet.firstOrNull { lut.keys.contains(checkNotNull(edges[it]).source) } - check( - entry != null) { "Unreachable node(s) present: $waitSet\nedges: $edges\nlut: $lut" } - waitSet.remove(entry) - val edge = checkNotNull(edges[entry]) - lut[entry] = arg.createSuccNode(lut[edge.source], edge.action, checkNotNull(nodes[entry]).state, - checkNotNull(nodes[entry]).target) - .also { n -> if (checkNotNull(nodes[entry]).expanded) n.expanded = true } + fun instantiate(partialOrd: PartialOrd): ARG { + val arg = ARG.create(partialOrd) + val lut = HashMap>() + initNodes.forEach { + lut[it.key] = + arg.createInitNode(it.value.first, it.value.second).also { n -> + if (it.value.third) n.expanded = true } - coveringEdges.forEach { checkNotNull(lut[it.key]).cover(lut[it.value]) } - return arg } + arg.initialized = true + val waitSet = HashSet(edges.keys) + while (waitSet.isNotEmpty()) { + val entry = waitSet.firstOrNull { lut.keys.contains(checkNotNull(edges[it]).source) } + check(entry != null) { "Unreachable node(s) present: $waitSet\nedges: $edges\nlut: $lut" } + waitSet.remove(entry) + val edge = checkNotNull(edges[entry]) + lut[entry] = + arg + .createSuccNode( + lut[edge.source], + edge.action, + checkNotNull(nodes[entry]).state, + checkNotNull(nodes[entry]).target, + ) + .also { n -> if (checkNotNull(nodes[entry]).expanded) n.expanded = true } + } + coveringEdges.forEach { checkNotNull(lut[it.key]).cover(lut[it.value]) } + return arg + } } +data class ArgNodeAdapter(val target: Boolean, val state: S, val expanded: Boolean) -data class ArgNodeAdapter( - val target: Boolean, - val state: S, - val expanded: Boolean -) - -data class ArgEdgeAdapter( - val source: Int, - val action: A, -) { - -} \ No newline at end of file +data class ArgEdgeAdapter(val source: Int, val action: A) {} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/StateAdapters.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/StateAdapters.kt index a75f2133cb..bef973890b 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/StateAdapters.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/StateAdapters.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -35,69 +34,68 @@ import hu.bme.mit.theta.grammar.dsl.expr.ExpressionWrapper class ExplStateAdapter(val scope: Scope, val env: Env) : TypeAdapter() { - override fun write(writer: JsonWriter, value: ExplState) { - writer.beginObject() - writer.name("bottom").value(value.isBottom) - writer.name("decls").beginArray() - value.toMap().forEach { - writer.beginObject().name(it.key.name).value(it.value.toString()).endObject() - } - writer.endArray().endObject() + override fun write(writer: JsonWriter, value: ExplState) { + writer.beginObject() + writer.name("bottom").value(value.isBottom) + writer.name("decls").beginArray() + value.toMap().forEach { + writer.beginObject().name(it.key.name).value(it.value.toString()).endObject() } + writer.endArray().endObject() + } - override fun read(reader: JsonReader): ExplState { - var ret: ExplState? = null - reader.beginObject() - check(reader.nextName() == "bottom") - if (reader.nextBoolean()) ret = ExplState.bottom() - check(reader.nextName() == "decls") - reader.beginArray() - val mutableValuation = MutableValuation() - while (reader.peek() != JsonToken.END_ARRAY) { - reader.beginObject() - val name = reader.nextName() - val variable: VarDecl<*> = env.eval(scope.resolve(name).orElseThrow()) as VarDecl<*> - val value = ExpressionWrapper(scope, reader.nextString()).instantiate(env) as LitExpr<*> - mutableValuation.put(variable, value) - reader.endObject() - } - if (ret == null) ret = ExplState.of(mutableValuation) - reader.endArray() - reader.endObject() - return ret!! + override fun read(reader: JsonReader): ExplState { + var ret: ExplState? = null + reader.beginObject() + check(reader.nextName() == "bottom") + if (reader.nextBoolean()) ret = ExplState.bottom() + check(reader.nextName() == "decls") + reader.beginArray() + val mutableValuation = MutableValuation() + while (reader.peek() != JsonToken.END_ARRAY) { + reader.beginObject() + val name = reader.nextName() + val variable: VarDecl<*> = env.eval(scope.resolve(name).orElseThrow()) as VarDecl<*> + val value = ExpressionWrapper(scope, reader.nextString()).instantiate(env) as LitExpr<*> + mutableValuation.put(variable, value) + reader.endObject() } - + if (ret == null) ret = ExplState.of(mutableValuation) + reader.endArray() + reader.endObject() + return ret!! + } } class PredStateAdapter(val gsonSupplier: () -> Gson, val scope: Scope, val env: Env) : - TypeAdapter() { + TypeAdapter() { - lateinit var gson: Gson - override fun write(writer: JsonWriter, value: PredState) { - initGson() - writer.beginObject() - writer.name("bottom").value(value.isBottom) - writer.name("preds") - gson.toJson(gson.toJsonTree(value.preds), writer) - writer.endObject() - } + lateinit var gson: Gson - override fun read(reader: JsonReader): PredState { - initGson() - var ret: PredState? = null - reader.beginObject() - check(reader.nextName() == "bottom") - if (reader.nextBoolean()) ret = PredState.bottom() - check(reader.nextName() == "preds") - val preds = gson.fromJson>>(reader, - object : TypeToken>>() {}.type) - if (ret == null) ret = PredState.of(preds) - reader.endObject() - return ret!! - } + override fun write(writer: JsonWriter, value: PredState) { + initGson() + writer.beginObject() + writer.name("bottom").value(value.isBottom) + writer.name("preds") + gson.toJson(gson.toJsonTree(value.preds), writer) + writer.endObject() + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } + override fun read(reader: JsonReader): PredState { + initGson() + var ret: PredState? = null + reader.beginObject() + check(reader.nextName() == "bottom") + if (reader.nextBoolean()) ret = PredState.bottom() + check(reader.nextName() == "preds") + val preds = + gson.fromJson>>(reader, object : TypeToken>>() {}.type) + if (ret == null) ret = PredState.of(preds) + reader.endObject() + return ret!! + } -} \ No newline at end of file + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/TraceAdapter.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/TraceAdapter.kt index 0571800b23..ec99225711 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/TraceAdapter.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/TraceAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -26,37 +25,46 @@ import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.Trace import java.lang.reflect.Type -class TraceAdapter(val gsonSupplier: () -> Gson, private val stateTypeSupplier: () -> Type, - private val actionType: Type) : TypeAdapter>() { +class TraceAdapter( + val gsonSupplier: () -> Gson, + private val stateTypeSupplier: () -> Type, + private val actionType: Type, +) : TypeAdapter>() { - private lateinit var gson: Gson - private lateinit var stateType: Type + private lateinit var gson: Gson + private lateinit var stateType: Type - override fun write(writer: JsonWriter, value: Trace<*, *>) { - initGson() - writer.beginObject() - writer.name("states") - gson.toJson(gson.toJsonTree(value.states), writer) - writer.name("actions") - gson.toJson(gson.toJsonTree(value.actions), writer) - writer.endObject() - } + override fun write(writer: JsonWriter, value: Trace<*, *>) { + initGson() + writer.beginObject() + writer.name("states") + gson.toJson(gson.toJsonTree(value.states), writer) + writer.name("actions") + gson.toJson(gson.toJsonTree(value.actions), writer) + writer.endObject() + } - override fun read(reader: JsonReader): Trace<*, *> { - initGson() - if (!this::stateType.isInitialized) stateType = stateTypeSupplier() - reader.beginObject() - check(reader.nextName() == "states") - val stateList: List = gson.fromJson(reader, - TypeToken.getParameterized(TypeToken.get(List::class.java).type, stateType).type) - check(reader.nextName() == "actions") - val actionList: List = gson.fromJson(reader, - TypeToken.getParameterized(TypeToken.get(List::class.java).type, actionType).type) - reader.endObject() - return Trace.of(stateList, actionList) - } + override fun read(reader: JsonReader): Trace<*, *> { + initGson() + if (!this::stateType.isInitialized) stateType = stateTypeSupplier() + reader.beginObject() + check(reader.nextName() == "states") + val stateList: List = + gson.fromJson( + reader, + TypeToken.getParameterized(TypeToken.get(List::class.java).type, stateType).type, + ) + check(reader.nextName() == "actions") + val actionList: List = + gson.fromJson( + reader, + TypeToken.getParameterized(TypeToken.get(List::class.java).type, actionType).type, + ) + reader.endObject() + return Trace.of(stateList, actionList) + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } -} \ No newline at end of file + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/VarDeclAdapter.kt b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/VarDeclAdapter.kt index 8e0ea55885..6789015a9a 100644 --- a/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/VarDeclAdapter.kt +++ b/subprojects/common/grammar/src/main/java/hu/bme/mit/theta/grammar/gson/VarDeclAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -29,50 +28,54 @@ import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.type.Type -class VarDeclAdapter(val gsonSupplier: () -> Gson, val scope: MutableScope, val env: Env, - val throwIfNotInScope: Boolean = false) : TypeAdapter>() { +class VarDeclAdapter( + val gsonSupplier: () -> Gson, + val scope: MutableScope, + val env: Env, + val throwIfNotInScope: Boolean = false, +) : TypeAdapter>() { - private lateinit var gson: Gson + private lateinit var gson: Gson - override fun write(writer: JsonWriter, value: VarDecl<*>) { - initGson() - writer.beginObject() - writer.name("name").value(value.name) - writer.name("type") - gson.toJson(gson.toJsonTree(value.type), writer) - writer.endObject() - } + override fun write(writer: JsonWriter, value: VarDecl<*>) { + initGson() + writer.beginObject() + writer.name("name").value(value.name) + writer.name("type") + gson.toJson(gson.toJsonTree(value.type), writer) + writer.endObject() + } - override fun read(reader: JsonReader): VarDecl<*> { - initGson() - reader.beginObject() - lateinit var name: String - lateinit var type: Type - while (reader.peek() != JsonToken.END_OBJECT) { - val key = reader.nextName() - if (key == "name") { - name = reader.nextString() - } else if (key == "type") { - val jsonType = object : TypeToken() {}.type - type = gson.fromJson(reader, jsonType) - } - } - reader.endObject() - val symbol = scope.resolve(name) - if (symbol.isPresent) { - val ret: VarDecl<*> = env.eval(symbol.get()) as VarDecl<*> - check(ret.type == type) - return ret - } - check(!throwIfNotInScope) { "Variable $name is not known." } - val newSymbol = Symbol { name } - val varDecl = Var(name, type) - scope.add(newSymbol) - env.define(newSymbol, varDecl) - return varDecl + override fun read(reader: JsonReader): VarDecl<*> { + initGson() + reader.beginObject() + lateinit var name: String + lateinit var type: Type + while (reader.peek() != JsonToken.END_OBJECT) { + val key = reader.nextName() + if (key == "name") { + name = reader.nextString() + } else if (key == "type") { + val jsonType = object : TypeToken() {}.type + type = gson.fromJson(reader, jsonType) + } } - - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() + reader.endObject() + val symbol = scope.resolve(name) + if (symbol.isPresent) { + val ret: VarDecl<*> = env.eval(symbol.get()) as VarDecl<*> + check(ret.type == type) + return ret } -} \ No newline at end of file + check(!throwIfNotInScope) { "Variable $name is not known." } + val newSymbol = Symbol { name } + val varDecl = Var(name, type) + scope.add(newSymbol) + env.define(newSymbol, varDecl) + return varDecl + } + + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/ExprTest.kt b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/ExprTest.kt index ff72c48eb5..f0c9e82b4f 100644 --- a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/ExprTest.kt +++ b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/ExprTest.kt @@ -52,215 +52,276 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) class ExprTest { - @Parameterized.Parameter(0) - lateinit var memory: Expr<*> - - @Parameterized.Parameter(1) - lateinit var serialized: String - - @Parameterized.Parameter(2) - lateinit var decls: Map> - - companion object { - - @JvmStatic - @Parameterized.Parameters - fun data(): Collection> { - val x = Var("x", Int()) - val p = Param("p", Int()) - - val bvLit1 = Bv(BooleanArray(4) { it % 2 == 0 }) - val bvLit2 = Bv(BooleanArray(4) { it % 2 == 1 }) - val fpLit1 = FpLitExpr.of(true, bvLit1, Bv(BooleanArray(6) { it % 2 == 0 })) - val fpLit2 = FpLitExpr.of(false, bvLit1, Bv(BooleanArray(6) { it % 2 == 0 })) - - return listOf( - arrayOf(True(), "true", emptyMap>()), - arrayOf(False(), "false", emptyMap>()), - - arrayOf(Int(0), "0", emptyMap>()), - arrayOf(Int(10), "10", emptyMap>()), - arrayOf(Int(-1), "-1", emptyMap>()), - - arrayOf(Rat(1, 2), "1%2", emptyMap>()), - arrayOf(Rat(-1, 2), "-1%2", emptyMap>()), - arrayOf(Rat(10, -21), "-10%21", emptyMap>()), - - arrayOf(bvLit1, "#b1010", emptyMap>()), - - arrayOf(fpLit1, "(#b1 #b1010 #b101010)", emptyMap>()), - arrayOf(fpLit2, "(#b0 #b1010 #b101010)", emptyMap>()), - - arrayOf(ArrayLitExpr.of(listOf(), Int(2), ArrayType.of(Int(), Int())), - "(array (default 2))", emptyMap>()), - arrayOf(ArrayLitExpr.of(listOf(Tuple2.of(Int(0), Int(1))), Int(2), - ArrayType.of(Int(), Int())), "(array (0 1) (default 2))", - emptyMap>()), - arrayOf( - ArrayLitExpr.of(listOf(Tuple2.of(Int(0), Int(1)), Tuple2.of(Int(1), Int(2))), - Int(3), ArrayType.of(Int(), Int())), "(array (0 1) (1 2) (default 3))", - emptyMap>()), - - arrayOf(RefExpr.of(x), "x", mapOf(Pair(NamedSymbol("x"), x))), - - arrayOf(Ite(True(), Int(1), Int(2)), "(ite true 1 2)", - emptyMap>()), - arrayOf(Iff(True(), False()), "(iff true false)", emptyMap>()), - arrayOf(Imply(True(), False()), "(=> true false)", emptyMap>()), - arrayOf(Forall(listOf(p), True()), "(forall ((p Int)) true)", - mapOf(Pair(NamedSymbol("p"), p))), - arrayOf(Exists(listOf(p), True()), "(exists ((p Int)) true)", - mapOf(Pair(NamedSymbol("p"), p))), - - arrayOf(Max(fpLit1, fpLit2), "(fpmax (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - arrayOf(Min(fpLit1, fpLit2), "(fpmin (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - - arrayOf(Or(True(), False()), "(or true false)", emptyMap>()), - arrayOf(Xor(True(), False()), "(xor true false)", emptyMap>()), - arrayOf(And(True(), False(), False()), "(and true false false)", - emptyMap>()), - arrayOf(Not(True()), "(not true)", emptyMap>()), - - arrayOf(Eq(Int(1), Int(2)), "(= 1 2)", emptyMap>()), - arrayOf(Lt(Int(1), Int(2)), "(< 1 2)", emptyMap>()), - arrayOf(Leq(Int(1), Int(2)), "(<= 1 2)", emptyMap>()), - arrayOf(Gt(Int(1), Int(2)), "(> 1 2)", emptyMap>()), - arrayOf(Geq(Int(1), Int(2)), "(>= 1 2)", emptyMap>()), - - arrayOf(BvExprs.ULt(bvLit1, bvLit1), "(bvult #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.ULeq(bvLit1, bvLit1), "(bvule #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.UGt(bvLit1, bvLit1), "(bvugt #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.UGeq(bvLit1, bvLit1), "(bvuge #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.SLt(bvLit1, bvLit1), "(bvslt #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.SLeq(bvLit1, bvLit1), "(bvsle #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.SGt(bvLit1, bvLit1), "(bvsgt #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.SGeq(bvLit1, bvLit1), "(bvsge #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.Or(listOf(bvLit1, bvLit1)), "(bvor #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.Xor(listOf(bvLit1, bvLit1)), "(bvxor #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.And(listOf(bvLit1, bvLit1)), "(bvand #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.ShiftLeft(bvLit1, bvLit1), "(bvshl #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.ArithShiftRight(bvLit1, bvLit1), "(bvashr #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.LogicShiftRight(bvLit1, bvLit1), "(bvlshr #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.RotateLeft(bvLit1, bvLit1), "(bvrol #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.RotateRight(bvLit1, bvLit1), "(bvror #b1010 #b1010)", - emptyMap>()), - - arrayOf(Add(listOf(Int(1), Int(2), Int(3))), "(+ 1 2 3)", - emptyMap>()), - arrayOf(Sub(Int(1), Int(2)), "(- 1 2)", emptyMap>()), - arrayOf(Add(bvLit1, bvLit1), "(bvadd #b1010 #b1010)", emptyMap>()), - arrayOf(Sub(bvLit1, bvLit1), "(bvsub #b1010 #b1010)", emptyMap>()), - arrayOf(Add(fpLit1, fpLit2), "(fpadd (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - arrayOf(Sub(fpLit1, fpLit2), "(fpsub (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - - arrayOf(Mul(listOf(Int(1), Int(2), Int(3))), "(* 1 2 3)", - emptyMap>()), - arrayOf(Div(Int(1), Int(2)), "(div 1 2)", emptyMap>()), - arrayOf(Mod(Int(1), Int(2)), "(mod 1 2)", emptyMap>()), - arrayOf(Rem(Int(1), Int(2)), "(rem 1 2)", emptyMap>()), - arrayOf(Mul(bvLit1, bvLit1), "(bvmul #b1010 #b1010)", emptyMap>()), - arrayOf(UDiv(bvLit1, bvLit1), "(bvudiv #b1010 #b1010)", - emptyMap>()), - arrayOf(BvExprs.SDiv(bvLit1, bvLit2), "(bvsdiv #b1010 #b0101)", - emptyMap>()), - arrayOf(BvExprs.SMod(bvLit1, bvLit2), "(bvsmod #b1010 #b0101)", - emptyMap>()), - arrayOf(BvExprs.URem(bvLit1, bvLit2), "(bvurem #b1010 #b0101)", - emptyMap>()), - arrayOf(BvExprs.SRem(bvLit1, bvLit2), "(bvsrem #b1010 #b0101)", - emptyMap>()), - arrayOf(Mul(fpLit1, fpLit2), "(fpmul (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - arrayOf(FpExprs.Div(FpRoundingMode.RNE, fpLit1, fpLit2), - "(fpdiv[rne] (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - arrayOf(FpExprs.Rem(fpLit1, fpLit2), - "(fprem (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", - emptyMap>()), - - arrayOf(Concat(listOf(bvLit1, bvLit2)), "(++ #b1010 #b0101)", - emptyMap>()), - arrayOf(ZExt(bvLit1, BvType(5)), "(bv_zero_extend #b1010 (Bv 5))", - emptyMap>()), - arrayOf(SExt(bvLit1, BvType(5)), "(bv_sign_extend #b1010 (Bv 5))", - emptyMap>()), - - arrayOf(Pos(Int(1)), "(+ 1)", emptyMap>()), - arrayOf(Neg(Int(1)), "(- 1)", emptyMap>()), - arrayOf(Pos(bvLit1), "(bvpos #b1010)", emptyMap>()), - arrayOf(Neg(bvLit1), "(bvneg #b1010)", emptyMap>()), - arrayOf(Not(bvLit1), "(bvnot #b1010)", emptyMap>()), - - arrayOf(ArrayReadExpr.create( - ArrayLitExpr.of(emptyList(), Int(2), ArrayType.of(Int(), Int())), Int(5)), - "(read (array (default 2)) 5)", emptyMap>()), - arrayOf(ArrayWriteExpr.create( - ArrayLitExpr.of(emptyList(), Int(2), - ArrayType.of(Int(), Int())), Int(5), Int(6)), - "(write (array (default 2)) 5 6)", emptyMap>()), - - arrayOf(Prime(Int(1)), "(prime 1)", emptyMap>()), - arrayOf(Extract(bvLit1, Int(1), Int(4)), "(extract #b1010 1 4)", - emptyMap>()), - - arrayOf(Dereference(Int(0), Int(1), Int()), "(deref 0 1 Int)", emptyMap>()), - ) - } - } - - @Test - fun testSerialize() { - Assert.assertEquals(serialized, memory.toString()) - } - - @Test - fun testDeserialize() { - if (decls.any { it.value is ParamDecl }) return - val symbolTable = SymbolTable() - decls.forEach { symbolTable.add(it.key) } - val env = Env() - decls.forEach { env.define(it.key, it.value) } - val expr = simplify( - ExpressionWrapper(SimpleScope(symbolTable), serialized).instantiate(env)) - Assert.assertEquals(simplify(memory), expr) - } - - @Test - fun testRoundTrip() { - if (decls.any { it.value is ParamDecl }) return - val symbolTable = SymbolTable() - decls.forEach { symbolTable.add(it.key) } - val env = Env() - decls.forEach { env.define(it.key, it.value) } - val expr = simplify( - ExpressionWrapper(SimpleScope(symbolTable), memory.toString()).instantiate(env)) - Assert.assertEquals(simplify(memory), expr) + @Parameterized.Parameter(0) lateinit var memory: Expr<*> + + @Parameterized.Parameter(1) lateinit var serialized: String + + @Parameterized.Parameter(2) lateinit var decls: Map> + + companion object { + + @JvmStatic + @Parameterized.Parameters + fun data(): Collection> { + val x = Var("x", Int()) + val p = Param("p", Int()) + + val bvLit1 = Bv(BooleanArray(4) { it % 2 == 0 }) + val bvLit2 = Bv(BooleanArray(4) { it % 2 == 1 }) + val fpLit1 = FpLitExpr.of(true, bvLit1, Bv(BooleanArray(6) { it % 2 == 0 })) + val fpLit2 = FpLitExpr.of(false, bvLit1, Bv(BooleanArray(6) { it % 2 == 0 })) + + return listOf( + arrayOf(True(), "true", emptyMap>()), + arrayOf(False(), "false", emptyMap>()), + arrayOf(Int(0), "0", emptyMap>()), + arrayOf(Int(10), "10", emptyMap>()), + arrayOf(Int(-1), "-1", emptyMap>()), + arrayOf(Rat(1, 2), "1%2", emptyMap>()), + arrayOf(Rat(-1, 2), "-1%2", emptyMap>()), + arrayOf(Rat(10, -21), "-10%21", emptyMap>()), + arrayOf(bvLit1, "#b1010", emptyMap>()), + arrayOf(fpLit1, "(#b1 #b1010 #b101010)", emptyMap>()), + arrayOf(fpLit2, "(#b0 #b1010 #b101010)", emptyMap>()), + arrayOf( + ArrayLitExpr.of(listOf(), Int(2), ArrayType.of(Int(), Int())), + "(array (default 2))", + emptyMap>(), + ), + arrayOf( + ArrayLitExpr.of(listOf(Tuple2.of(Int(0), Int(1))), Int(2), ArrayType.of(Int(), Int())), + "(array (0 1) (default 2))", + emptyMap>(), + ), + arrayOf( + ArrayLitExpr.of( + listOf(Tuple2.of(Int(0), Int(1)), Tuple2.of(Int(1), Int(2))), + Int(3), + ArrayType.of(Int(), Int()), + ), + "(array (0 1) (1 2) (default 3))", + emptyMap>(), + ), + arrayOf(RefExpr.of(x), "x", mapOf(Pair(NamedSymbol("x"), x))), + arrayOf( + Ite(True(), Int(1), Int(2)), + "(ite true 1 2)", + emptyMap>(), + ), + arrayOf(Iff(True(), False()), "(iff true false)", emptyMap>()), + arrayOf(Imply(True(), False()), "(=> true false)", emptyMap>()), + arrayOf( + Forall(listOf(p), True()), + "(forall ((p Int)) true)", + mapOf(Pair(NamedSymbol("p"), p)), + ), + arrayOf( + Exists(listOf(p), True()), + "(exists ((p Int)) true)", + mapOf(Pair(NamedSymbol("p"), p)), + ), + arrayOf( + Max(fpLit1, fpLit2), + "(fpmax (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf( + Min(fpLit1, fpLit2), + "(fpmin (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf(Or(True(), False()), "(or true false)", emptyMap>()), + arrayOf(Xor(True(), False()), "(xor true false)", emptyMap>()), + arrayOf( + And(True(), False(), False()), + "(and true false false)", + emptyMap>(), + ), + arrayOf(Not(True()), "(not true)", emptyMap>()), + arrayOf(Eq(Int(1), Int(2)), "(= 1 2)", emptyMap>()), + arrayOf(Lt(Int(1), Int(2)), "(< 1 2)", emptyMap>()), + arrayOf(Leq(Int(1), Int(2)), "(<= 1 2)", emptyMap>()), + arrayOf(Gt(Int(1), Int(2)), "(> 1 2)", emptyMap>()), + arrayOf(Geq(Int(1), Int(2)), "(>= 1 2)", emptyMap>()), + arrayOf(BvExprs.ULt(bvLit1, bvLit1), "(bvult #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.ULeq(bvLit1, bvLit1), "(bvule #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.UGt(bvLit1, bvLit1), "(bvugt #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.UGeq(bvLit1, bvLit1), "(bvuge #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.SLt(bvLit1, bvLit1), "(bvslt #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.SLeq(bvLit1, bvLit1), "(bvsle #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.SGt(bvLit1, bvLit1), "(bvsgt #b1010 #b1010)", emptyMap>()), + arrayOf(BvExprs.SGeq(bvLit1, bvLit1), "(bvsge #b1010 #b1010)", emptyMap>()), + arrayOf( + BvExprs.Or(listOf(bvLit1, bvLit1)), + "(bvor #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.Xor(listOf(bvLit1, bvLit1)), + "(bvxor #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.And(listOf(bvLit1, bvLit1)), + "(bvand #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.ShiftLeft(bvLit1, bvLit1), + "(bvshl #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.ArithShiftRight(bvLit1, bvLit1), + "(bvashr #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.LogicShiftRight(bvLit1, bvLit1), + "(bvlshr #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.RotateLeft(bvLit1, bvLit1), + "(bvrol #b1010 #b1010)", + emptyMap>(), + ), + arrayOf( + BvExprs.RotateRight(bvLit1, bvLit1), + "(bvror #b1010 #b1010)", + emptyMap>(), + ), + arrayOf(Add(listOf(Int(1), Int(2), Int(3))), "(+ 1 2 3)", emptyMap>()), + arrayOf(Sub(Int(1), Int(2)), "(- 1 2)", emptyMap>()), + arrayOf(Add(bvLit1, bvLit1), "(bvadd #b1010 #b1010)", emptyMap>()), + arrayOf(Sub(bvLit1, bvLit1), "(bvsub #b1010 #b1010)", emptyMap>()), + arrayOf( + Add(fpLit1, fpLit2), + "(fpadd (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf( + Sub(fpLit1, fpLit2), + "(fpsub (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf(Mul(listOf(Int(1), Int(2), Int(3))), "(* 1 2 3)", emptyMap>()), + arrayOf(Div(Int(1), Int(2)), "(div 1 2)", emptyMap>()), + arrayOf(Mod(Int(1), Int(2)), "(mod 1 2)", emptyMap>()), + arrayOf(Rem(Int(1), Int(2)), "(rem 1 2)", emptyMap>()), + arrayOf(Mul(bvLit1, bvLit1), "(bvmul #b1010 #b1010)", emptyMap>()), + arrayOf(UDiv(bvLit1, bvLit1), "(bvudiv #b1010 #b1010)", emptyMap>()), + arrayOf( + BvExprs.SDiv(bvLit1, bvLit2), + "(bvsdiv #b1010 #b0101)", + emptyMap>(), + ), + arrayOf( + BvExprs.SMod(bvLit1, bvLit2), + "(bvsmod #b1010 #b0101)", + emptyMap>(), + ), + arrayOf( + BvExprs.URem(bvLit1, bvLit2), + "(bvurem #b1010 #b0101)", + emptyMap>(), + ), + arrayOf( + BvExprs.SRem(bvLit1, bvLit2), + "(bvsrem #b1010 #b0101)", + emptyMap>(), + ), + arrayOf( + Mul(fpLit1, fpLit2), + "(fpmul (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf( + FpExprs.Div(FpRoundingMode.RNE, fpLit1, fpLit2), + "(fpdiv[rne] (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf( + FpExprs.Rem(fpLit1, fpLit2), + "(fprem (#b1 #b1010 #b101010) (#b0 #b1010 #b101010))", + emptyMap>(), + ), + arrayOf(Concat(listOf(bvLit1, bvLit2)), "(++ #b1010 #b0101)", emptyMap>()), + arrayOf( + ZExt(bvLit1, BvType(5)), + "(bv_zero_extend #b1010 (Bv 5))", + emptyMap>(), + ), + arrayOf( + SExt(bvLit1, BvType(5)), + "(bv_sign_extend #b1010 (Bv 5))", + emptyMap>(), + ), + arrayOf(Pos(Int(1)), "(+ 1)", emptyMap>()), + arrayOf(Neg(Int(1)), "(- 1)", emptyMap>()), + arrayOf(Pos(bvLit1), "(bvpos #b1010)", emptyMap>()), + arrayOf(Neg(bvLit1), "(bvneg #b1010)", emptyMap>()), + arrayOf(Not(bvLit1), "(bvnot #b1010)", emptyMap>()), + arrayOf( + ArrayReadExpr.create( + ArrayLitExpr.of(emptyList(), Int(2), ArrayType.of(Int(), Int())), + Int(5), + ), + "(read (array (default 2)) 5)", + emptyMap>(), + ), + arrayOf( + ArrayWriteExpr.create( + ArrayLitExpr.of(emptyList(), Int(2), ArrayType.of(Int(), Int())), + Int(5), + Int(6), + ), + "(write (array (default 2)) 5 6)", + emptyMap>(), + ), + arrayOf(Prime(Int(1)), "(prime 1)", emptyMap>()), + arrayOf( + Extract(bvLit1, Int(1), Int(4)), + "(extract #b1010 1 4)", + emptyMap>(), + ), + arrayOf(Dereference(Int(0), Int(1), Int()), "(deref 0 1 Int)", emptyMap>()), + ) } - - data class NamedSymbol(val _name: String) : Symbol { - - override fun getName(): String { - return _name - } - + } + + @Test + fun testSerialize() { + Assert.assertEquals(serialized, memory.toString()) + } + + @Test + fun testDeserialize() { + if (decls.any { it.value is ParamDecl }) return + val symbolTable = SymbolTable() + decls.forEach { symbolTable.add(it.key) } + val env = Env() + decls.forEach { env.define(it.key, it.value) } + val expr = simplify(ExpressionWrapper(SimpleScope(symbolTable), serialized).instantiate(env)) + Assert.assertEquals(simplify(memory), expr) + } + + @Test + fun testRoundTrip() { + if (decls.any { it.value is ParamDecl }) return + val symbolTable = SymbolTable() + decls.forEach { symbolTable.add(it.key) } + val env = Env() + decls.forEach { env.define(it.key, it.value) } + val expr = + simplify(ExpressionWrapper(SimpleScope(symbolTable), memory.toString()).instantiate(env)) + Assert.assertEquals(simplify(memory), expr) + } + + data class NamedSymbol(val _name: String) : Symbol { + + override fun getName(): String { + return _name } -} \ No newline at end of file + } +} diff --git a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/StmtTest.kt b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/StmtTest.kt index 372e1f796e..72b7181a69 100644 --- a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/StmtTest.kt +++ b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/StmtTest.kt @@ -35,68 +35,68 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) class StmtTest { - @Parameterized.Parameter(0) - lateinit var memory: Stmt + @Parameterized.Parameter(0) lateinit var memory: Stmt - @Parameterized.Parameter(1) - lateinit var serialized: String + @Parameterized.Parameter(1) lateinit var serialized: String - @Parameterized.Parameter(2) - lateinit var decls: Map> + @Parameterized.Parameter(2) lateinit var decls: Map> - companion object { + companion object { - @JvmStatic - @Parameterized.Parameters - fun data(): Collection> { - val x = Var("x", Int()) - val addr = x.hashCode() + @JvmStatic + @Parameterized.Parameters + fun data(): Collection> { + val x = Var("x", Int()) + val addr = x.hashCode() - return listOf( - arrayOf(Assign(x, Int(1)), "(assign x 1)", - mapOf(Pair(ExprTest.NamedSymbol("x"), x))), - arrayOf(MemoryAssign(Dereference(Int(addr), Int(0), Int()), Int(1)), - "(memassign (deref $addr 0 Int) 1)", - mapOf(Pair(ExprTest.NamedSymbol("x"), x))), - arrayOf(Assume(Eq(x.ref, Int(1))), "(assume (= x 1))", - mapOf(Pair(ExprTest.NamedSymbol("x"), x))), - arrayOf(Havoc(x), "(havoc x)", mapOf(Pair(ExprTest.NamedSymbol("x"), x))), - ) - } + return listOf( + arrayOf(Assign(x, Int(1)), "(assign x 1)", mapOf(Pair(ExprTest.NamedSymbol("x"), x))), + arrayOf( + MemoryAssign(Dereference(Int(addr), Int(0), Int()), Int(1)), + "(memassign (deref $addr 0 Int) 1)", + mapOf(Pair(ExprTest.NamedSymbol("x"), x)), + ), + arrayOf( + Assume(Eq(x.ref, Int(1))), + "(assume (= x 1))", + mapOf(Pair(ExprTest.NamedSymbol("x"), x)), + ), + arrayOf(Havoc(x), "(havoc x)", mapOf(Pair(ExprTest.NamedSymbol("x"), x))), + ) } + } - @Test - fun testSerialize() { - Assert.assertEquals(serialized, memory.toString()) - } - - @Test - fun testDeserialize() { - if (decls.any { it.value is ParamDecl }) return - val symbolTable = SymbolTable() - decls.forEach { symbolTable.add(it.key) } - val env = Env() - decls.forEach { env.define(it.key, it.value) } - val stmt = StatementWrapper(serialized, SimpleScope(symbolTable)).instantiate(env) - Assert.assertEquals(memory, stmt) - } + @Test + fun testSerialize() { + Assert.assertEquals(serialized, memory.toString()) + } - @Test - fun testRoundTrip() { - if (decls.any { it.value is ParamDecl }) return - val symbolTable = SymbolTable() - decls.forEach { symbolTable.add(it.key) } - val env = Env() - decls.forEach { env.define(it.key, it.value) } - val stmt = StatementWrapper(memory.toString(), SimpleScope(symbolTable)).instantiate(env) - Assert.assertEquals(memory, stmt) - } + @Test + fun testDeserialize() { + if (decls.any { it.value is ParamDecl }) return + val symbolTable = SymbolTable() + decls.forEach { symbolTable.add(it.key) } + val env = Env() + decls.forEach { env.define(it.key, it.value) } + val stmt = StatementWrapper(serialized, SimpleScope(symbolTable)).instantiate(env) + Assert.assertEquals(memory, stmt) + } - data class NamedSymbol(val _name: String) : Symbol { + @Test + fun testRoundTrip() { + if (decls.any { it.value is ParamDecl }) return + val symbolTable = SymbolTable() + decls.forEach { symbolTable.add(it.key) } + val env = Env() + decls.forEach { env.define(it.key, it.value) } + val stmt = StatementWrapper(memory.toString(), SimpleScope(symbolTable)).instantiate(env) + Assert.assertEquals(memory, stmt) + } - override fun getName(): String { - return _name - } + data class NamedSymbol(val _name: String) : Symbol { + override fun getName(): String { + return _name } -} \ No newline at end of file + } +} diff --git a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/TypeTest.kt b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/TypeTest.kt index a94dfbf86d..9232a2b034 100644 --- a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/TypeTest.kt +++ b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/dsl/TypeTest.kt @@ -32,45 +32,40 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) class TypeTest { - @Parameterized.Parameter(0) - lateinit var memory: Type + @Parameterized.Parameter(0) lateinit var memory: Type - @Parameterized.Parameter(1) - lateinit var serialized: String + @Parameterized.Parameter(1) lateinit var serialized: String - companion object { + companion object { - @JvmStatic - @Parameterized.Parameters - fun data(): Collection> { - return listOf( - arrayOf(Int(), "Int"), - arrayOf(Rat(), "Rat"), - arrayOf(Bool(), "Bool"), - arrayOf(Func(Int(), Rat()), "(Func Int Rat)"), - arrayOf(ArrayExprs.Array(Int(), Rat()), "(Array ([Int] -> Rat))"), - arrayOf(BvType(32), "(Bv 32)"), - arrayOf(FpType(12, 45), "(Fp 12 45)"), - - arrayOf(Func(Int(), ArrayExprs.Array(Int(), Rat())), - "(Func Int (Array ([Int] -> Rat)))"), - ) - } + @JvmStatic + @Parameterized.Parameters + fun data(): Collection> { + return listOf( + arrayOf(Int(), "Int"), + arrayOf(Rat(), "Rat"), + arrayOf(Bool(), "Bool"), + arrayOf(Func(Int(), Rat()), "(Func Int Rat)"), + arrayOf(ArrayExprs.Array(Int(), Rat()), "(Array ([Int] -> Rat))"), + arrayOf(BvType(32), "(Bv 32)"), + arrayOf(FpType(12, 45), "(Fp 12 45)"), + arrayOf(Func(Int(), ArrayExprs.Array(Int(), Rat())), "(Func Int (Array ([Int] -> Rat)))"), + ) } + } - @Test - fun testSerialize() { - Assert.assertEquals(serialized, memory.toString()) - } + @Test + fun testSerialize() { + Assert.assertEquals(serialized, memory.toString()) + } - @Test - fun testDeserialize() { - Assert.assertEquals(TypeWrapper(serialized).instantiate(), memory) - } - - @Test - fun testRoundTrip() { - Assert.assertEquals(TypeWrapper(memory.toString()).instantiate(), memory) - } + @Test + fun testDeserialize() { + Assert.assertEquals(TypeWrapper(serialized).instantiate(), memory) + } -} \ No newline at end of file + @Test + fun testRoundTrip() { + Assert.assertEquals(TypeWrapper(memory.toString()).instantiate(), memory) + } +} diff --git a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/gson/TestGson.kt b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/gson/TestGson.kt index cb6ca93071..be9268ba6c 100644 --- a/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/gson/TestGson.kt +++ b/subprojects/common/grammar/src/test/java/hu/bme/mit/theta/grammar/gson/TestGson.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.grammar.gson import com.google.gson.Gson @@ -24,8 +23,8 @@ import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter import hu.bme.mit.theta.analysis.PartialOrd import hu.bme.mit.theta.analysis.Trace -import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.algorithm.SafetyResult +import hu.bme.mit.theta.analysis.algorithm.arg.ARG import hu.bme.mit.theta.analysis.expl.ExplOrd import hu.bme.mit.theta.analysis.expl.ExplState import hu.bme.mit.theta.analysis.expr.StmtAction @@ -47,198 +46,242 @@ import hu.bme.mit.theta.grammar.dsl.SimpleScope import hu.bme.mit.theta.grammar.dsl.expr.ExpressionWrapper import hu.bme.mit.theta.grammar.dsl.stmt.StatementWrapper import hu.bme.mit.theta.grammar.dsl.type.TypeWrapper -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test import java.lang.reflect.Type import java.util.* +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test class TestGson { - companion object { - - private fun explArgAdapterHelper(): Type = - TypeToken.getParameterized( - TypeToken.get(ArgAdapterHelper::class.java).type, - TypeToken.get(ExplState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun explArgHelper(): Type = - TypeToken.getParameterized( - TypeToken.get(ARG::class.java).type, - TypeToken.get(ExplState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun explTraceHelper() = - TypeToken.getParameterized( - TypeToken.get(Trace::class.java).type, - TypeToken.get(ExplState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun predArgAdapterHelper(): Type = - TypeToken.getParameterized( - TypeToken.get(ArgAdapterHelper::class.java).type, - TypeToken.get(PredState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun predArgHelper(): Type = - TypeToken.getParameterized( - TypeToken.get(ARG::class.java).type, - TypeToken.get(PredState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun predTraceHelper() = - TypeToken.getParameterized( - TypeToken.get(Trace::class.java).type, - TypeToken.get(PredState::class.java).type, - TypeToken.get(SimpleStmtAction::class.java).type, - ).type - - private fun getExplBuilder(gsonSuppl: () -> Gson): GsonBuilder { - val gsonBuilder = GsonBuilder() - - gsonBuilder.registerTypeHierarchyAdapter(ARG::class.java, - ArgAdapter(gsonSuppl, { ExplOrd.getInstance() }, { explArgAdapterHelper() })) - gsonBuilder.registerTypeHierarchyAdapter(Trace::class.java, - TraceAdapter(gsonSuppl, { ExplState::class.java }, SimpleStmtAction::class.java)) - gsonBuilder.registerTypeHierarchyAdapter(SafetyResult::class.java, - SafetyResultAdapter(gsonSuppl, { explArgHelper() }, { explTraceHelper() })) - - return gsonBuilder - } - - private fun getPredBuilder(gsonSuppl: () -> Gson): GsonBuilder { - val gsonBuilder = GsonBuilder() - - gsonBuilder.registerTypeHierarchyAdapter(ARG::class.java, - ArgAdapter(gsonSuppl, { PartialOrd { a, b -> true } }, { predArgAdapterHelper() })) - gsonBuilder.registerTypeHierarchyAdapter(Trace::class.java, - TraceAdapter(gsonSuppl, { PredState::class.java }, SimpleStmtAction::class.java)) - gsonBuilder.registerTypeHierarchyAdapter(SafetyResult::class.java, - SafetyResultAdapter(gsonSuppl, { predArgHelper() }, { predTraceHelper() })) - - return gsonBuilder - } - - val x = Var("x", Int()) - private fun getGson(gsonBuilder: GsonBuilder, gsonSuppl: () -> Gson): Gson { - val symbolTable = SymbolTable() - val symbol = ExprTest.NamedSymbol(x.name) - symbolTable.add(symbol) - val scope = SimpleScope(symbolTable) - val env = Env() - env.define(symbol, x) - - gsonBuilder.registerTypeHierarchyAdapter(VarDecl::class.java, - VarDeclAdapter(gsonSuppl, scope, env, false)) - gsonBuilder.registerTypeHierarchyAdapter(Stmt::class.java, - StringTypeAdapter { StatementWrapper(it, scope).instantiate(env) }) - gsonBuilder.registerTypeHierarchyAdapter(Expr::class.java, - StringTypeAdapter { ExpressionWrapper(scope, it).instantiate(env) }) - gsonBuilder.registerTypeHierarchyAdapter(hu.bme.mit.theta.core.type.Type::class.java, - StringTypeAdapter { TypeWrapper(it).instantiate() }) - gsonBuilder.registerTypeHierarchyAdapter(VarIndexing::class.java, - StringTypeAdapter { BasicVarIndexing.fromString(it, scope, env) }) - gsonBuilder.registerTypeHierarchyAdapter(ExplState::class.java, ExplStateAdapter(scope, env)) - gsonBuilder.registerTypeHierarchyAdapter(PredState::class.java, - PredStateAdapter(gsonSuppl, scope, env)) - gsonBuilder.registerTypeHierarchyAdapter(Pair::class.java, PairAdapter(gsonSuppl)) - gsonBuilder.registerTypeHierarchyAdapter(Triple::class.java, TripleAdapter(gsonSuppl)) - gsonBuilder.registerTypeHierarchyAdapter(Optional::class.java, OptionalAdapter(gsonSuppl)) - - gsonBuilder.registerTypeAdapter(SimpleStmtAction::class.java, SimpleStmtActionAdapter(gsonSuppl)) - - return gsonBuilder.create() - } - } + companion object { - @Test - fun testExplArgAdapter() { - lateinit var gson: Gson; - gson = getGson(getExplBuilder { gson }) { gson } - - val arg = ARG.create(ExplOrd.getInstance()) - - val initNode = arg.createInitNode(ExplState.bottom(), false) - val otherNode = arg.createSuccNode(initNode, SimpleStmtAction(SkipStmt.getInstance()), ExplState.top(), false) - - val serialized = gson.toJson(arg) - val parsedBack = gson.fromJson(serialized, ARG::class.java) - Assertions.assertEquals(arg.initNodes.count(), parsedBack.initNodes.count()) - Assertions.assertEquals(arg.nodes.count(), parsedBack.nodes.count()) - } - - @Test - fun testPredArgAdapter() { - lateinit var gson: Gson; - gson = getGson(getPredBuilder { gson }) { gson } - - val arg = ARG.create { _, _ -> false } + private fun explArgAdapterHelper(): Type = + TypeToken.getParameterized( + TypeToken.get(ArgAdapterHelper::class.java).type, + TypeToken.get(ExplState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, + ) + .type - val initNode = arg.createInitNode(PredState.bottom(), false) - val otherNode = arg.createSuccNode(initNode, SimpleStmtAction(Havoc(x)), PredState.of(), false) + private fun explArgHelper(): Type = + TypeToken.getParameterized( + TypeToken.get(ARG::class.java).type, + TypeToken.get(ExplState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, + ) + .type - val serialized = gson.toJson(arg) - val parsedBack = gson.fromJson(serialized, ARG::class.java) - Assertions.assertEquals(arg.initNodes.count(), parsedBack.initNodes.count()) - Assertions.assertEquals(arg.nodes.count(), parsedBack.nodes.count()) - } + private fun explTraceHelper() = + TypeToken.getParameterized( + TypeToken.get(Trace::class.java).type, + TypeToken.get(ExplState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, + ) + .type - @Test - fun testExplTrace() { - lateinit var gson: Gson; - gson = getGson(getExplBuilder { gson }) { gson } + private fun predArgAdapterHelper(): Type = + TypeToken.getParameterized( + TypeToken.get(ArgAdapterHelper::class.java).type, + TypeToken.get(PredState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, + ) + .type - val trace = Trace.of( - listOf(ExplState.of(ImmutableValuation.builder().put(x, Int(1)).build()), - ExplState.of(ImmutableValuation.builder().put(x, Int(2)).build())), - listOf(SimpleStmtAction(SkipStmt.getInstance())) + private fun predArgHelper(): Type = + TypeToken.getParameterized( + TypeToken.get(ARG::class.java).type, + TypeToken.get(PredState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, ) + .type - val serialized = gson.toJson(trace) - val parsedBack = gson.fromJson(serialized, Trace::class.java) - Assertions.assertEquals(trace.length(), parsedBack.length()) + private fun predTraceHelper() = + TypeToken.getParameterized( + TypeToken.get(Trace::class.java).type, + TypeToken.get(PredState::class.java).type, + TypeToken.get(SimpleStmtAction::class.java).type, + ) + .type + + private fun getExplBuilder(gsonSuppl: () -> Gson): GsonBuilder { + val gsonBuilder = GsonBuilder() + + gsonBuilder.registerTypeHierarchyAdapter( + ARG::class.java, + ArgAdapter(gsonSuppl, { ExplOrd.getInstance() }, { explArgAdapterHelper() }), + ) + gsonBuilder.registerTypeHierarchyAdapter( + Trace::class.java, + TraceAdapter(gsonSuppl, { ExplState::class.java }, SimpleStmtAction::class.java), + ) + gsonBuilder.registerTypeHierarchyAdapter( + SafetyResult::class.java, + SafetyResultAdapter(gsonSuppl, { explArgHelper() }, { explTraceHelper() }), + ) + + return gsonBuilder } - @Test - fun testOther() { - lateinit var gson: Gson; - gson = getGson(getPredBuilder { gson }) { gson } - - val pair = Pair("a", "b") - val triple = Triple("a", "b", "c") - val opt = Optional.of(pair) - - Assertions.assertEquals(pair, gson.fromJson(gson.toJson(pair), Pair::class.java)) - Assertions.assertEquals(triple, gson.fromJson(gson.toJson(triple), Triple::class.java)) - Assertions.assertEquals(opt, gson.fromJson(gson.toJson(opt), Optional::class.java)) - Assertions.assertEquals(x, gson.fromJson(gson.toJson(x), VarDecl::class.java)) - + private fun getPredBuilder(gsonSuppl: () -> Gson): GsonBuilder { + val gsonBuilder = GsonBuilder() + + gsonBuilder.registerTypeHierarchyAdapter( + ARG::class.java, + ArgAdapter( + gsonSuppl, + { PartialOrd { a, b -> true } }, + { predArgAdapterHelper() }, + ), + ) + gsonBuilder.registerTypeHierarchyAdapter( + Trace::class.java, + TraceAdapter(gsonSuppl, { PredState::class.java }, SimpleStmtAction::class.java), + ) + gsonBuilder.registerTypeHierarchyAdapter( + SafetyResult::class.java, + SafetyResultAdapter(gsonSuppl, { predArgHelper() }, { predTraceHelper() }), + ) + + return gsonBuilder } - - class SimpleStmtAction(val stmt: Stmt) : StmtAction() { - - override fun getStmts(): List { - return listOf(stmt); - } + val x = Var("x", Int()) + + private fun getGson(gsonBuilder: GsonBuilder, gsonSuppl: () -> Gson): Gson { + val symbolTable = SymbolTable() + val symbol = ExprTest.NamedSymbol(x.name) + symbolTable.add(symbol) + val scope = SimpleScope(symbolTable) + val env = Env() + env.define(symbol, x) + + gsonBuilder.registerTypeHierarchyAdapter( + VarDecl::class.java, + VarDeclAdapter(gsonSuppl, scope, env, false), + ) + gsonBuilder.registerTypeHierarchyAdapter( + Stmt::class.java, + StringTypeAdapter { StatementWrapper(it, scope).instantiate(env) }, + ) + gsonBuilder.registerTypeHierarchyAdapter( + Expr::class.java, + StringTypeAdapter { ExpressionWrapper(scope, it).instantiate(env) }, + ) + gsonBuilder.registerTypeHierarchyAdapter( + hu.bme.mit.theta.core.type.Type::class.java, + StringTypeAdapter { TypeWrapper(it).instantiate() }, + ) + gsonBuilder.registerTypeHierarchyAdapter( + VarIndexing::class.java, + StringTypeAdapter { BasicVarIndexing.fromString(it, scope, env) }, + ) + gsonBuilder.registerTypeHierarchyAdapter(ExplState::class.java, ExplStateAdapter(scope, env)) + gsonBuilder.registerTypeHierarchyAdapter( + PredState::class.java, + PredStateAdapter(gsonSuppl, scope, env), + ) + gsonBuilder.registerTypeHierarchyAdapter(Pair::class.java, PairAdapter(gsonSuppl)) + gsonBuilder.registerTypeHierarchyAdapter( + Triple::class.java, + TripleAdapter(gsonSuppl), + ) + gsonBuilder.registerTypeHierarchyAdapter( + Optional::class.java, + OptionalAdapter(gsonSuppl), + ) + + gsonBuilder.registerTypeAdapter( + SimpleStmtAction::class.java, + SimpleStmtActionAdapter(gsonSuppl), + ) + + return gsonBuilder.create() } + } + + @Test + fun testExplArgAdapter() { + lateinit var gson: Gson + gson = getGson(getExplBuilder { gson }) { gson } + + val arg = ARG.create(ExplOrd.getInstance()) + + val initNode = arg.createInitNode(ExplState.bottom(), false) + val otherNode = + arg.createSuccNode(initNode, SimpleStmtAction(SkipStmt.getInstance()), ExplState.top(), false) + + val serialized = gson.toJson(arg) + val parsedBack = gson.fromJson(serialized, ARG::class.java) + Assertions.assertEquals(arg.initNodes.count(), parsedBack.initNodes.count()) + Assertions.assertEquals(arg.nodes.count(), parsedBack.nodes.count()) + } + + @Test + fun testPredArgAdapter() { + lateinit var gson: Gson + gson = getGson(getPredBuilder { gson }) { gson } + + val arg = ARG.create { _, _ -> false } + + val initNode = arg.createInitNode(PredState.bottom(), false) + val otherNode = arg.createSuccNode(initNode, SimpleStmtAction(Havoc(x)), PredState.of(), false) + + val serialized = gson.toJson(arg) + val parsedBack = gson.fromJson(serialized, ARG::class.java) + Assertions.assertEquals(arg.initNodes.count(), parsedBack.initNodes.count()) + Assertions.assertEquals(arg.nodes.count(), parsedBack.nodes.count()) + } + + @Test + fun testExplTrace() { + lateinit var gson: Gson + gson = getGson(getExplBuilder { gson }) { gson } + + val trace = + Trace.of( + listOf( + ExplState.of(ImmutableValuation.builder().put(x, Int(1)).build()), + ExplState.of(ImmutableValuation.builder().put(x, Int(2)).build()), + ), + listOf(SimpleStmtAction(SkipStmt.getInstance())), + ) + + val serialized = gson.toJson(trace) + val parsedBack = gson.fromJson(serialized, Trace::class.java) + Assertions.assertEquals(trace.length(), parsedBack.length()) + } + + @Test + fun testOther() { + lateinit var gson: Gson + gson = getGson(getPredBuilder { gson }) { gson } + + val pair = Pair("a", "b") + val triple = Triple("a", "b", "c") + val opt = Optional.of(pair) + + Assertions.assertEquals(pair, gson.fromJson(gson.toJson(pair), Pair::class.java)) + Assertions.assertEquals(triple, gson.fromJson(gson.toJson(triple), Triple::class.java)) + Assertions.assertEquals(opt, gson.fromJson(gson.toJson(opt), Optional::class.java)) + Assertions.assertEquals(x, gson.fromJson(gson.toJson(x), VarDecl::class.java)) + } + + class SimpleStmtAction(val stmt: Stmt) : StmtAction() { + + override fun getStmts(): List { + return listOf(stmt) + } + } - class SimpleStmtActionAdapter(val gsonUtils: () -> Gson) : TypeAdapter() { - - override fun write(out: JsonWriter, value: SimpleStmtAction) { - gsonUtils().toJson(gsonUtils().toJsonTree(value.stmt, Stmt::class.java), out) - } - - override fun read(`in`: JsonReader): SimpleStmtAction { - val stmt = gsonUtils().fromJson(`in`, Stmt::class.java) - return SimpleStmtAction(stmt) - } + class SimpleStmtActionAdapter(val gsonUtils: () -> Gson) : TypeAdapter() { + override fun write(out: JsonWriter, value: SimpleStmtAction) { + gsonUtils().toJson(gsonUtils().toJsonTree(value.stmt, Stmt::class.java), out) } -} \ No newline at end of file + override fun read(`in`: JsonReader): SimpleStmtAction { + val stmt = gsonUtils().fromJson(`in`, Stmt::class.java) + return SimpleStmtAction(stmt) + } + } +} diff --git a/subprojects/common/multi-tests/src/test/kotlin/multi/MultiNondetDiningPhilosophersTest.kt b/subprojects/common/multi-tests/src/test/kotlin/multi/MultiNondetDiningPhilosophersTest.kt index ce30555dea..6bfde67da4 100644 --- a/subprojects/common/multi-tests/src/test/kotlin/multi/MultiNondetDiningPhilosophersTest.kt +++ b/subprojects/common/multi-tests/src/test/kotlin/multi/MultiNondetDiningPhilosophersTest.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package multi import hu.bme.mit.theta.analysis.expl.ExplInitFunc @@ -41,84 +40,110 @@ import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.solver.Solver import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory +import java.io.FileInputStream import org.junit.Test import org.junit.jupiter.api.Assertions -import java.io.FileInputStream class MultiNondetDiningPhilosophersTest { - val logger: Logger = ConsoleLogger(Logger.Level.SUBSTEP) - val solver: Solver = Z3LegacySolverFactory.getInstance().createSolver() + val logger: Logger = ConsoleLogger(Logger.Level.SUBSTEP) + val solver: Solver = Z3LegacySolverFactory.getInstance().createSolver() - @Test - fun test() { - var phil1cfa: CFA - FileInputStream("src/test/resources/cfa/philosopher1.cfa").use { inputStream -> - phil1cfa = CfaDslManager.createCfa(inputStream) - } - var phil2rawCfa: CFA - FileInputStream("src/test/resources/cfa/philosopher2.cfa").use { inputStream -> - phil2rawCfa = CfaDslManager.createCfa(inputStream) - } - var phil3rawCfa: CFA - FileInputStream("src/test/resources/cfa/philosopher3.cfa").use { inputStream -> - phil3rawCfa = CfaDslManager.createCfa(inputStream) - } - var phil4rawCfa: CFA - FileInputStream("src/test/resources/cfa/philosopher4.cfa").use { inputStream -> - phil4rawCfa = CfaDslManager.createCfa(inputStream) - } - val variables = phil1cfa.vars - val phil2cfa = phil2rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) - val phil3cfa = phil3rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) - val phil4cfa = phil4rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) + @Test + fun test() { + var phil1cfa: CFA + FileInputStream("src/test/resources/cfa/philosopher1.cfa").use { inputStream -> + phil1cfa = CfaDslManager.createCfa(inputStream) + } + var phil2rawCfa: CFA + FileInputStream("src/test/resources/cfa/philosopher2.cfa").use { inputStream -> + phil2rawCfa = CfaDslManager.createCfa(inputStream) + } + var phil3rawCfa: CFA + FileInputStream("src/test/resources/cfa/philosopher3.cfa").use { inputStream -> + phil3rawCfa = CfaDslManager.createCfa(inputStream) + } + var phil4rawCfa: CFA + FileInputStream("src/test/resources/cfa/philosopher4.cfa").use { inputStream -> + phil4rawCfa = CfaDslManager.createCfa(inputStream) + } + val variables = phil1cfa.vars + val phil2cfa = phil2rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) + val phil3cfa = phil3rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) + val phil4cfa = phil4rawCfa.copyWithReplacingVars(variables.associateBy { it.name }) - val cfa1ConfigBuilder = CfaConfigBuilder(CfaConfigBuilder.Domain.EXPL, CfaConfigBuilder.Refinement.SEQ_ITP, - Z3LegacySolverFactory.getInstance()) - cfa1ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) - val cfa1ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil1cfa) - val cfa2ConfigBuilder = CfaConfigBuilder(CfaConfigBuilder.Domain.EXPL, CfaConfigBuilder.Refinement.SEQ_ITP, - Z3LegacySolverFactory.getInstance()) - cfa2ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) - val cfa2ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil2cfa) - val cfa3ConfigBuilder = CfaConfigBuilder(CfaConfigBuilder.Domain.EXPL, CfaConfigBuilder.Refinement.SEQ_ITP, - Z3LegacySolverFactory.getInstance()) - cfa3ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) - val cfa3ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil3cfa) - val cfa4ConfigBuilder = CfaConfigBuilder(CfaConfigBuilder.Domain.EXPL, CfaConfigBuilder.Refinement.SEQ_ITP, - Z3LegacySolverFactory.getInstance()) - cfa4ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) - val cfa4ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil4cfa) + val cfa1ConfigBuilder = + CfaConfigBuilder( + CfaConfigBuilder.Domain.EXPL, + CfaConfigBuilder.Refinement.SEQ_ITP, + Z3LegacySolverFactory.getInstance(), + ) + cfa1ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) + val cfa1ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil1cfa) + val cfa2ConfigBuilder = + CfaConfigBuilder( + CfaConfigBuilder.Domain.EXPL, + CfaConfigBuilder.Refinement.SEQ_ITP, + Z3LegacySolverFactory.getInstance(), + ) + cfa2ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) + val cfa2ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil2cfa) + val cfa3ConfigBuilder = + CfaConfigBuilder( + CfaConfigBuilder.Domain.EXPL, + CfaConfigBuilder.Refinement.SEQ_ITP, + Z3LegacySolverFactory.getInstance(), + ) + cfa3ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) + val cfa3ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil3cfa) + val cfa4ConfigBuilder = + CfaConfigBuilder( + CfaConfigBuilder.Domain.EXPL, + CfaConfigBuilder.Refinement.SEQ_ITP, + Z3LegacySolverFactory.getInstance(), + ) + cfa4ConfigBuilder.encoding(CfaConfigBuilder.Encoding.LBE) + val cfa4ExplBuilder = cfa1ConfigBuilder.ExplStrategy(phil4cfa) - val cfaRefToPrec = RefutationToGlobalCfaPrec(ItpRefToExplPrec(), phil1cfa.initLoc) - val dataInitPrec = ExplPrec.of(variables) - val cfaInitPrec: CfaPrec = GlobalCfaPrec.create(dataInitPrec) + val cfaRefToPrec = RefutationToGlobalCfaPrec(ItpRefToExplPrec(), phil1cfa.initLoc) + val dataInitPrec = ExplPrec.of(variables) + val cfaInitPrec: CfaPrec = GlobalCfaPrec.create(dataInitPrec) - var initExpr: Expr = True() - variables.filter { it.name.contains("inited") }.forEach { initExpr = Eq(it.ref, False()) } + var initExpr: Expr = True() + variables.filter { it.name.contains("inited") }.forEach { initExpr = Eq(it.ref, False()) } - val product1 = StmtMultiBuilder(cfa1ExplBuilder.multiSide, cfa1ExplBuilder.lts) - .addRightSide(cfa2ExplBuilder.multiSide, cfa2ExplBuilder.lts) - .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) - val product2 = StmtMultiBuilder(cfa3ExplBuilder.multiSide, cfa3ExplBuilder.lts) - .addRightSide(cfa4ExplBuilder.multiSide, cfa4ExplBuilder.lts) - .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) - val totalProduct = StmtMultiBuilder(product1.side, product1.lts) - .addRightSide(product2.side, product2.lts) - .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) + val product1 = + StmtMultiBuilder(cfa1ExplBuilder.multiSide, cfa1ExplBuilder.lts) + .addRightSide(cfa2ExplBuilder.multiSide, cfa2ExplBuilder.lts) + .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) + val product2 = + StmtMultiBuilder(cfa3ExplBuilder.multiSide, cfa3ExplBuilder.lts) + .addRightSide(cfa4ExplBuilder.multiSide, cfa4ExplBuilder.lts) + .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) + val totalProduct = + StmtMultiBuilder(product1.side, product1.lts) + .addRightSide(product2.side, product2.lts) + .build(NextSideFunctions.Nondet(), ExplInitFunc.create(solver, initExpr)) - var prop: Expr = True() - variables.forEach { prop = And(prop, Eq(it.ref, True())) } - val dataPredicate = ExplStatePredicate(prop, solver) - val multiConfigBuilder = StmtMultiConfigBuilder.ItpStmtMultiConfigBuilder(totalProduct, prop, - MultiStatePredicate(dataPredicate), - RefToMultiPrec(cfaRefToPrec, cfaRefToPrec, ItpRefToExplPrec()), - RefToMultiPrec(cfaRefToPrec, cfaRefToPrec, ItpRefToExplPrec()), ItpRefToExplPrec(), - MultiPrec(cfaInitPrec, cfaInitPrec, dataInitPrec), - MultiPrec(cfaInitPrec, cfaInitPrec, dataInitPrec), dataInitPrec, Z3LegacySolverFactory.getInstance(), - logger) - val result = multiConfigBuilder.build().check() + var prop: Expr = True() + variables.forEach { prop = And(prop, Eq(it.ref, True())) } + val dataPredicate = ExplStatePredicate(prop, solver) + val multiConfigBuilder = + StmtMultiConfigBuilder.ItpStmtMultiConfigBuilder( + totalProduct, + prop, + MultiStatePredicate(dataPredicate), + RefToMultiPrec(cfaRefToPrec, cfaRefToPrec, ItpRefToExplPrec()), + RefToMultiPrec(cfaRefToPrec, cfaRefToPrec, ItpRefToExplPrec()), + ItpRefToExplPrec(), + MultiPrec(cfaInitPrec, cfaInitPrec, dataInitPrec), + MultiPrec(cfaInitPrec, cfaInitPrec, dataInitPrec), + dataInitPrec, + Z3LegacySolverFactory.getInstance(), + logger, + ) + val result = multiConfigBuilder.build().check() - Assertions.assertTrue(result.isUnsafe) - } -} \ No newline at end of file + Assertions.assertTrue(result.isUnsafe) + } +} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/CStatistics.kt b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/CStatistics.kt index 6be63aab98..563878ffc2 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/CStatistics.kt +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/CStatistics.kt @@ -20,156 +20,158 @@ import hu.bme.mit.theta.core.type.abstracttype.DivExpr import hu.bme.mit.theta.core.type.abstracttype.MulExpr import hu.bme.mit.theta.frontend.transformation.model.statements.* -data class CStatistics( - val globalDeclarations: Int, - val functions: Collection, -) +data class CStatistics(val globalDeclarations: Int, val functions: Collection) data class CFunctionStatistics( - val variables: Int, - val loopNumber: Int, - val deepestLoop: Int, - val linear: Boolean, + val variables: Int, + val loopNumber: Int, + val deepestLoop: Int, + val linear: Boolean, ) fun CProgram.getStatistics(): CStatistics { - return CStatistics( - globalDeclarations = globalDeclarations.size, - functions = functions.map { - val (loopNumber, deepestLoop, linear) = it.collectStatistics() - CFunctionStatistics( - variables = it.flatVariables.size, - loopNumber = loopNumber, - deepestLoop = deepestLoop, - linear = linear - ) - } - ) + return CStatistics( + globalDeclarations = globalDeclarations.size, + functions = + functions.map { + val (loopNumber, deepestLoop, linear) = it.collectStatistics() + CFunctionStatistics( + variables = it.flatVariables.size, + loopNumber = loopNumber, + deepestLoop = deepestLoop, + linear = linear, + ) + }, + ) } fun CFunction.collectStatistics(): Triple { - val statisticsCollectorVisitor = StatisticsCollectorVisitor() - this.compound?.accept(statisticsCollectorVisitor, Unit) - return Triple(statisticsCollectorVisitor.loopNumber, statisticsCollectorVisitor.deepestLoop, - statisticsCollectorVisitor.linear) + val statisticsCollectorVisitor = StatisticsCollectorVisitor() + this.compound?.accept(statisticsCollectorVisitor, Unit) + return Triple( + statisticsCollectorVisitor.loopNumber, + statisticsCollectorVisitor.deepestLoop, + statisticsCollectorVisitor.linear, + ) } fun Expr<*>.isNonLinear(): Boolean { - if (this is MulExpr<*> || this is DivExpr<*>) { - return true; - } else if (this.ops.size > 0) { - return this.ops.any { it.isNonLinear() } - } else return false; + if (this is MulExpr<*> || this is DivExpr<*>) { + return true + } else if (this.ops.size > 0) { + return this.ops.any { it.isNonLinear() } + } else return false } class StatisticsCollectorVisitor : CStatementVisitor { - var loopNumber = 0 - private set - var deepestLoop = 0 - private set - var linear = true - private set - - private var currentDepth = 0 - - override fun visit(statement: CAssignment, param: Unit) { - val lval = statement.getlValue() - if (linear && lval.isNonLinear()) linear = false - statement.getrValue()?.accept(this, param) - } - - override fun visit(statement: CAssume, param: Unit) { - if (linear && statement.assumeStmt.cond.isNonLinear()) linear = false - } - - override fun visit(statement: CBreak, param: Unit) { - } - - override fun visit(statement: CCall, param: Unit) { - statement.params.forEach { it?.accept(this, param) } - } - - override fun visit(statement: CCase, param: Unit) { - statement.expr?.accept(this, param) - statement.statement?.accept(this, param) - } - - override fun visit(statement: CCompound, param: Unit) { - statement.getcStatementList().forEach { it?.accept(this, param) } - } - - override fun visit(statement: CContinue, param: Unit) { - } - - override fun visit(statement: CDecls, param: Unit) { - statement.getcDeclarations().map { it.get1().initExpr?.accept(this, param) } - } - - override fun visit(statement: CDefault, param: Unit) { - statement.statement?.accept(this, param) - } - - override fun visit(statement: CDoWhile, param: Unit) { - loopNumber++ - statement.guard?.accept(this, param) - currentDepth++ - if (deepestLoop < currentDepth) deepestLoop = currentDepth - statement.body?.accept(this, param) - currentDepth-- - } - - override fun visit(statement: CExpr, param: Unit) { - if (linear && statement.expr?.isNonLinear() == true) linear = false - } - - override fun visit(statement: CFor, param: Unit) { - loopNumber++ - statement.init?.accept(this, param) - statement.guard?.accept(this, param) - statement.increment?.accept(this, param) - currentDepth++ - if (deepestLoop < currentDepth) deepestLoop = currentDepth - statement.body?.accept(this, param) - currentDepth-- - } - - override fun visit(statement: CFunction, param: Unit) { - System.err.println("WARNING: Should not be here (CFUnction embedding not supported)") - } - - override fun visit(statement: CGoto, param: Unit) { - } - - override fun visit(statement: CIf, param: Unit) { - statement.guard?.accept(this, param) - statement.body?.accept(this, param) - } - - override fun visit(statement: CInitializerList, param: Unit) { - statement.statements.forEach { it.get1().map { it?.accept(this, param) }; it.get2()?.accept(this, param) } - } - - override fun visit(statement: CProgram, param: Unit) { - System.err.println("WARNING: Should not be here (CProgram embedding not supported)") - } - - override fun visit(statement: CRet, param: Unit) { - statement.expr?.accept(this, param); - } - - override fun visit(statement: CSwitch, param: Unit) { - statement.testValue?.accept(this, param) - statement.body?.accept(this, param) - } - - override fun visit(statement: CWhile, param: Unit) { - loopNumber++ - statement.guard?.accept(this, param) - currentDepth++ - if (deepestLoop < currentDepth) deepestLoop = currentDepth - statement.body?.accept(this, param) - currentDepth-- - } + var loopNumber = 0 + private set + + var deepestLoop = 0 + private set + + var linear = true + private set + + private var currentDepth = 0 + + override fun visit(statement: CAssignment, param: Unit) { + val lval = statement.getlValue() + if (linear && lval.isNonLinear()) linear = false + statement.getrValue()?.accept(this, param) + } + + override fun visit(statement: CAssume, param: Unit) { + if (linear && statement.assumeStmt.cond.isNonLinear()) linear = false + } + + override fun visit(statement: CBreak, param: Unit) {} + + override fun visit(statement: CCall, param: Unit) { + statement.params.forEach { it?.accept(this, param) } + } + + override fun visit(statement: CCase, param: Unit) { + statement.expr?.accept(this, param) + statement.statement?.accept(this, param) + } + + override fun visit(statement: CCompound, param: Unit) { + statement.getcStatementList().forEach { it?.accept(this, param) } + } + + override fun visit(statement: CContinue, param: Unit) {} + + override fun visit(statement: CDecls, param: Unit) { + statement.getcDeclarations().map { it.get1().initExpr?.accept(this, param) } + } + + override fun visit(statement: CDefault, param: Unit) { + statement.statement?.accept(this, param) + } + + override fun visit(statement: CDoWhile, param: Unit) { + loopNumber++ + statement.guard?.accept(this, param) + currentDepth++ + if (deepestLoop < currentDepth) deepestLoop = currentDepth + statement.body?.accept(this, param) + currentDepth-- + } + + override fun visit(statement: CExpr, param: Unit) { + if (linear && statement.expr?.isNonLinear() == true) linear = false + } + + override fun visit(statement: CFor, param: Unit) { + loopNumber++ + statement.init?.accept(this, param) + statement.guard?.accept(this, param) + statement.increment?.accept(this, param) + currentDepth++ + if (deepestLoop < currentDepth) deepestLoop = currentDepth + statement.body?.accept(this, param) + currentDepth-- + } + + override fun visit(statement: CFunction, param: Unit) { + System.err.println("WARNING: Should not be here (CFUnction embedding not supported)") + } + + override fun visit(statement: CGoto, param: Unit) {} + + override fun visit(statement: CIf, param: Unit) { + statement.guard?.accept(this, param) + statement.body?.accept(this, param) + } + + override fun visit(statement: CInitializerList, param: Unit) { + statement.statements.forEach { + it.get1().map { it?.accept(this, param) } + it.get2()?.accept(this, param) + } + } + + override fun visit(statement: CProgram, param: Unit) { + System.err.println("WARNING: Should not be here (CProgram embedding not supported)") + } + + override fun visit(statement: CRet, param: Unit) { + statement.expr?.accept(this, param) + } + + override fun visit(statement: CSwitch, param: Unit) { + statement.testValue?.accept(this, param) + statement.body?.accept(this, param) + } + + override fun visit(statement: CWhile, param: Unit) { + loopNumber++ + statement.guard?.accept(this, param) + currentDepth++ + if (deepestLoop < currentDepth) deepestLoop = currentDepth + statement.body?.accept(this, param) + currentDepth-- + } } - diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java index 609518ea3e..39c85365fb 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/ExpressionVisitor.java @@ -13,9 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.frontend.transformation.grammar.expression; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.*; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Div; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Mod; +import static hu.bme.mit.theta.core.type.anytype.Exprs.Reference; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.FpType; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; + import hu.bme.mit.theta.c.frontend.dsl.gen.CBaseVisitor; import hu.bme.mit.theta.c.frontend.dsl.gen.CParser; import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.*; @@ -52,22 +60,12 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct; -import org.kframework.mpfr.BigFloat; -import org.kframework.mpfr.BinaryMathContext; - import java.math.BigInteger; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkState; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Div; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Mod; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.*; -import static hu.bme.mit.theta.core.type.anytype.Exprs.Reference; -import static hu.bme.mit.theta.core.type.fptype.FpExprs.FpType; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; +import org.kframework.mpfr.BigFloat; +import org.kframework.mpfr.BinaryMathContext; public class ExpressionVisitor extends CBaseVisitor> { protected final List preStatements = new ArrayList<>(); @@ -81,7 +79,14 @@ public class ExpressionVisitor extends CBaseVisitor> { private final PostfixVisitor postfixVisitor; private final Logger uniqueWarningLogger; - public ExpressionVisitor(ParseContext parseContext, FunctionVisitor functionVisitor, Deque>>> variables, Map, CDeclaration> functions, TypedefVisitor typedefVisitor, TypeVisitor typeVisitor, Logger uniqueWarningLogger) { + public ExpressionVisitor( + ParseContext parseContext, + FunctionVisitor functionVisitor, + Deque>>> variables, + Map, CDeclaration> functions, + TypedefVisitor typedefVisitor, + TypeVisitor typeVisitor, + Logger uniqueWarningLogger) { this.parseContext = parseContext; this.functionVisitor = functionVisitor; this.variables = variables; @@ -116,12 +121,20 @@ public List getPreStatements() { @Override public Expr visitLogicalOrExpression(CParser.LogicalOrExpressionContext ctx) { if (ctx.logicalAndExpression().size() > 1) { - List> collect = ctx.logicalAndExpression().stream().map(logicalAndExpressionContext -> { - Expr expr = logicalAndExpressionContext.accept(this); - return AbstractExprs.Neq(CComplexType.getType(expr, parseContext).getNullValue(), expr); - }).collect(Collectors.toList()); + List> collect = + ctx.logicalAndExpression().stream() + .map( + logicalAndExpressionContext -> { + Expr expr = logicalAndExpressionContext.accept(this); + return AbstractExprs.Neq( + CComplexType.getType(expr, parseContext) + .getNullValue(), + expr); + }) + .collect(Collectors.toList()); CComplexType signedInt = CComplexType.getSignedInt(parseContext); - IteExpr ite = Ite(BoolExprs.Or(collect), signedInt.getUnitValue(), signedInt.getNullValue()); + IteExpr ite = + Ite(BoolExprs.Or(collect), signedInt.getUnitValue(), signedInt.getNullValue()); parseContext.getMetadata().create(ite, "cType", signedInt); return ite; } @@ -131,12 +144,20 @@ public Expr visitLogicalOrExpression(CParser.LogicalOrExpressionContext ctx) @Override public Expr visitLogicalAndExpression(CParser.LogicalAndExpressionContext ctx) { if (ctx.inclusiveOrExpression().size() > 1) { - List> collect = ctx.inclusiveOrExpression().stream().map(inclusiveOrExpression -> { - Expr expr = inclusiveOrExpression.accept(this); - return AbstractExprs.Neq(CComplexType.getType(expr, parseContext).getNullValue(), expr); - }).collect(Collectors.toList()); + List> collect = + ctx.inclusiveOrExpression().stream() + .map( + inclusiveOrExpression -> { + Expr expr = inclusiveOrExpression.accept(this); + return AbstractExprs.Neq( + CComplexType.getType(expr, parseContext) + .getNullValue(), + expr); + }) + .collect(Collectors.toList()); CComplexType signedInt = CComplexType.getSignedInt(parseContext); - IteExpr ite = Ite(BoolExprs.And(collect), signedInt.getUnitValue(), signedInt.getNullValue()); + IteExpr ite = + Ite(BoolExprs.And(collect), signedInt.getUnitValue(), signedInt.getNullValue()); parseContext.getMetadata().create(ite, "cType", signedInt); return ite; } @@ -146,15 +167,28 @@ public Expr visitLogicalAndExpression(CParser.LogicalAndExpressionContext ctx @Override public Expr visitInclusiveOrExpression(CParser.InclusiveOrExpressionContext ctx) { if (ctx.exclusiveOrExpression().size() > 1) { - List> exprs = ctx.exclusiveOrExpression().stream().map(exclusiveOrExpression -> exclusiveOrExpression.accept(this)).collect(Collectors.toList()); - List types = exprs.stream().map((Expr expr1) -> CComplexType.getType(expr1, parseContext)).collect(Collectors.toList()); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(types, parseContext); - List> collect = exprs.stream().map(expr -> { - Expr ret = smallestCommonType.castTo(expr); - checkState(ret.getType() instanceof BvType, "Non-bitvector type found!"); - //noinspection unchecked - return (Expr) ret; - }).collect(Collectors.toList()); + List> exprs = + ctx.exclusiveOrExpression().stream() + .map(exclusiveOrExpression -> exclusiveOrExpression.accept(this)) + .collect(Collectors.toList()); + List types = + exprs.stream() + .map((Expr expr1) -> CComplexType.getType(expr1, parseContext)) + .collect(Collectors.toList()); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType(types, parseContext); + List> collect = + exprs.stream() + .map( + expr -> { + Expr ret = smallestCommonType.castTo(expr); + checkState( + ret.getType() instanceof BvType, + "Non-bitvector type found!"); + //noinspection unchecked + return (Expr) ret; + }) + .collect(Collectors.toList()); BvOrExpr or = BvExprs.Or(collect); parseContext.getMetadata().create(or, "cType", smallestCommonType); return or; @@ -165,15 +199,28 @@ public Expr visitInclusiveOrExpression(CParser.InclusiveOrExpressionContext c @Override public Expr visitExclusiveOrExpression(CParser.ExclusiveOrExpressionContext ctx) { if (ctx.andExpression().size() > 1) { - List> exprs = ctx.andExpression().stream().map(andExpression -> andExpression.accept(this)).collect(Collectors.toList()); - List types = exprs.stream().map((Expr expr1) -> CComplexType.getType(expr1, parseContext)).collect(Collectors.toList()); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(types, parseContext); - List> collect = exprs.stream().map(expr -> { - Expr ret = smallestCommonType.castTo(expr); - checkState(ret.getType() instanceof BvType, "Non-bitvector type found!"); - //noinspection unchecked - return (Expr) ret; - }).collect(Collectors.toList()); + List> exprs = + ctx.andExpression().stream() + .map(andExpression -> andExpression.accept(this)) + .collect(Collectors.toList()); + List types = + exprs.stream() + .map((Expr expr1) -> CComplexType.getType(expr1, parseContext)) + .collect(Collectors.toList()); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType(types, parseContext); + List> collect = + exprs.stream() + .map( + expr -> { + Expr ret = smallestCommonType.castTo(expr); + checkState( + ret.getType() instanceof BvType, + "Non-bitvector type found!"); + //noinspection unchecked + return (Expr) ret; + }) + .collect(Collectors.toList()); BvXorExpr xor = BvExprs.Xor(collect); parseContext.getMetadata().create(xor, "cType", smallestCommonType); return xor; @@ -184,15 +231,28 @@ public Expr visitExclusiveOrExpression(CParser.ExclusiveOrExpressionContext c @Override public Expr visitAndExpression(CParser.AndExpressionContext ctx) { if (ctx.equalityExpression().size() > 1) { - List> exprs = ctx.equalityExpression().stream().map(equalityExpression -> equalityExpression.accept(this)).collect(Collectors.toList()); - List types = exprs.stream().map((Expr expr1) -> CComplexType.getType(expr1, parseContext)).collect(Collectors.toList()); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(types, parseContext); - List> collect = exprs.stream().map(expr -> { - Expr ret = smallestCommonType.castTo(expr); - checkState(ret.getType() instanceof BvType, "Non-bitvector type found!"); - //noinspection unchecked - return (Expr) ret; - }).collect(Collectors.toList()); + List> exprs = + ctx.equalityExpression().stream() + .map(equalityExpression -> equalityExpression.accept(this)) + .collect(Collectors.toList()); + List types = + exprs.stream() + .map((Expr expr1) -> CComplexType.getType(expr1, parseContext)) + .collect(Collectors.toList()); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType(types, parseContext); + List> collect = + exprs.stream() + .map( + expr -> { + Expr ret = smallestCommonType.castTo(expr); + checkState( + ret.getType() instanceof BvType, + "Non-bitvector type found!"); + //noinspection unchecked + return (Expr) ret; + }) + .collect(Collectors.toList()); BvAndExpr and = BvExprs.And(collect); parseContext.getMetadata().create(and, "cType", smallestCommonType); return and; @@ -206,19 +266,30 @@ public Expr visitEqualityExpression(CParser.EqualityExpressionContext ctx) { Expr expr = null; for (int i = 0; i < ctx.relationalExpression().size() - 1; ++i) { Expr leftOp, rightOp; - if (expr == null) - leftOp = ctx.relationalExpression(i).accept(this); - else - leftOp = expr; + if (expr == null) leftOp = ctx.relationalExpression(i).accept(this); + else leftOp = expr; rightOp = ctx.relationalExpression(i + 1).accept(this); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(leftOp, parseContext), CComplexType.getType(rightOp, parseContext)), parseContext); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType( + List.of( + CComplexType.getType(leftOp, parseContext), + CComplexType.getType(rightOp, parseContext)), + parseContext); Expr leftExpr = smallestCommonType.castTo(leftOp); Expr rightExpr = smallestCommonType.castTo(rightOp); CComplexType signedInt = CComplexType.getSignedInt(parseContext); if (ctx.signs.get(i).getText().equals("==")) - expr = Ite(AbstractExprs.Eq(leftExpr, rightExpr), signedInt.getUnitValue(), signedInt.getNullValue()); + expr = + Ite( + AbstractExprs.Eq(leftExpr, rightExpr), + signedInt.getUnitValue(), + signedInt.getNullValue()); else - expr = Ite(AbstractExprs.Neq(leftExpr, rightExpr), signedInt.getUnitValue(), signedInt.getNullValue()); + expr = + Ite( + AbstractExprs.Neq(leftExpr, rightExpr), + signedInt.getUnitValue(), + signedInt.getNullValue()); parseContext.getMetadata().create(expr, "cType", signedInt); } return expr; @@ -232,12 +303,15 @@ public Expr visitRelationalExpression(CParser.RelationalExpressionContext ctx Expr expr = null; for (int i = 0; i < ctx.shiftExpression().size() - 1; ++i) { Expr leftOp, rightOp; - if (expr == null) - leftOp = ctx.shiftExpression(i).accept(this); - else - leftOp = expr; + if (expr == null) leftOp = ctx.shiftExpression(i).accept(this); + else leftOp = expr; rightOp = ctx.shiftExpression(i + 1).accept(this); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(leftOp, parseContext), CComplexType.getType(rightOp, parseContext)), parseContext); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType( + List.of( + CComplexType.getType(leftOp, parseContext), + CComplexType.getType(rightOp, parseContext)), + parseContext); Expr leftExpr = smallestCommonType.castTo(leftOp); Expr rightExpr = smallestCommonType.castTo(rightOp); Expr guard; @@ -255,9 +329,12 @@ public Expr visitRelationalExpression(CParser.RelationalExpressionContext ctx guard = AbstractExprs.Geq(leftExpr, rightExpr); break; default: - throw new UnsupportedFrontendElementException("Unexpected relational expression sign: " + ctx.signs.get(i).getText()); + throw new UnsupportedFrontendElementException( + "Unexpected relational expression sign: " + + ctx.signs.get(i).getText()); } -// MaxEnumAnalyzer.instance.consume(guard); TODO: handle circular dependency + // MaxEnumAnalyzer.instance.consume(guard); TODO: handle circular + // dependency CComplexType signedInt = CComplexType.getSignedInt(parseContext); expr = Ite(guard, signedInt.getUnitValue(), signedInt.getNullValue()); parseContext.getMetadata().create(expr, "cType", signedInt); @@ -274,7 +351,9 @@ public Expr visitShiftExpression(CParser.ShiftExpressionContext ctx) { checkState(accept.getType() instanceof BvType); //noinspection unchecked Expr expr = (Expr) accept; - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(accept, parseContext)), parseContext); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType( + List.of(CComplexType.getType(accept, parseContext)), parseContext); checkState(smallestCommonType.getSmtType() instanceof BvType); for (int i = 1; i < ctx.additiveExpression().size(); ++i) { Expr rightOp; @@ -282,10 +361,16 @@ public Expr visitShiftExpression(CParser.ShiftExpressionContext ctx) { checkState(accept.getType() instanceof BvType); //noinspection unchecked rightOp = (Expr) accept; - Expr leftExpr = cast(smallestCommonType.castTo(expr), (BvType) smallestCommonType.getSmtType()); - Expr rightExpr = cast(smallestCommonType.castTo(rightOp), (BvType) smallestCommonType.getSmtType()); + Expr leftExpr = + cast( + smallestCommonType.castTo(expr), + (BvType) smallestCommonType.getSmtType()); + Expr rightExpr = + cast( + smallestCommonType.castTo(rightOp), + (BvType) smallestCommonType.getSmtType()); if (ctx.signs.get(i - 1).getText().equals(">>")) { - //TODO: is this sound? + // TODO: is this sound? if (leftExpr.getType().getSigned()) { expr = BvExprs.ArithShiftRight(leftExpr, rightExpr); } else { @@ -304,12 +389,24 @@ public Expr visitShiftExpression(CParser.ShiftExpressionContext ctx) { @Override public Expr visitAdditiveExpression(CParser.AdditiveExpressionContext ctx) { if (ctx.multiplicativeExpression().size() > 1) { - List> exprs = ctx.multiplicativeExpression().stream().map(multiplicativeExpression -> multiplicativeExpression.accept(this)).collect(Collectors.toList()); - List types = exprs.stream().map((Expr expr1) -> CComplexType.getType(expr1, parseContext)).collect(Collectors.toList()); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(types, parseContext); + List> exprs = + ctx.multiplicativeExpression().stream() + .map(multiplicativeExpression -> multiplicativeExpression.accept(this)) + .collect(Collectors.toList()); + List types = + exprs.stream() + .map((Expr expr1) -> CComplexType.getType(expr1, parseContext)) + .collect(Collectors.toList()); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType(types, parseContext); List> collect = new ArrayList<>(); for (int i = 0; i < exprs.size(); i++) { - parseContext.getMetadata().create(exprs.get(i), "cType", CComplexType.getType(exprs.get(i), parseContext)); + parseContext + .getMetadata() + .create( + exprs.get(i), + "cType", + CComplexType.getType(exprs.get(i), parseContext)); Expr castTo = smallestCommonType.castTo(exprs.get(i)); if (i != 0 && ctx.signs.get(i - 1).getText().equals("-")) { castTo = AbstractExprs.Neg(castTo); @@ -331,12 +428,15 @@ public Expr visitMultiplicativeExpression(CParser.MultiplicativeExpressionCon Expr expr = null; for (int i = 0; i < ctx.castExpression().size() - 1; ++i) { Expr leftOp, rightOp; - if (expr == null) - leftOp = ctx.castExpression(i).accept(this); - else - leftOp = expr; + if (expr == null) leftOp = ctx.castExpression(i).accept(this); + else leftOp = expr; rightOp = ctx.castExpression(i + 1).accept(this); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(CComplexType.getType(leftOp, parseContext), CComplexType.getType(rightOp, parseContext)), parseContext); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType( + List.of( + CComplexType.getType(leftOp, parseContext), + CComplexType.getType(rightOp, parseContext)), + parseContext); Expr leftExpr = smallestCommonType.castTo(leftOp); Expr rightExpr = smallestCommonType.castTo(rightOp); switch (ctx.signs.get(i).getText()) { @@ -344,23 +444,28 @@ public Expr visitMultiplicativeExpression(CParser.MultiplicativeExpressionCon expr = AbstractExprs.Mul(leftExpr, rightExpr); break; case "/": - if (leftExpr.getType() instanceof IntType && rightExpr.getType() instanceof IntType) { + if (leftExpr.getType() instanceof IntType + && rightExpr.getType() instanceof IntType) { expr = createIntDiv(leftExpr, rightExpr); } else { expr = AbstractExprs.Div(leftExpr, rightExpr); } break; case "%": - if (leftExpr.getType() instanceof IntType && rightExpr.getType() instanceof IntType) { + if (leftExpr.getType() instanceof IntType + && rightExpr.getType() instanceof IntType) { expr = createIntMod(leftExpr, rightExpr); - } else if (leftExpr.getType() instanceof BvType && rightExpr.getType() instanceof BvType) { + } else if (leftExpr.getType() instanceof BvType + && rightExpr.getType() instanceof BvType) { expr = AbstractExprs.Rem(leftExpr, rightExpr); } else { expr = AbstractExprs.Mod(leftExpr, rightExpr); } break; default: - throw new UnsupportedFrontendElementException("Unexpected multiplicative expression sign: " + ctx.signs.get(i).getText()); + throw new UnsupportedFrontendElementException( + "Unexpected multiplicative expression sign: " + + ctx.signs.get(i).getText()); } parseContext.getMetadata().create(expr, "cType", smallestCommonType); expr = smallestCommonType.castTo(expr); @@ -380,18 +485,20 @@ public Expr visitMultiplicativeExpression(CParser.MultiplicativeExpressionCon */ private Expr createIntDiv(Expr a, Expr b) { DivExpr aDivB = Div(a, b); - return Ite(Geq(a, Int(0)), // if (a >= 0) - aDivB, // a div b + return Ite( + Geq(a, Int(0)), // if (a >= 0) + aDivB, // a div b // else - Ite(Neq(Mod(a, b), Int(0)), // if (a mod b != 0) - Ite(Geq(b, Int(0)), // if (b >= 0) - Add(aDivB, Int(1)), // a div b + 1 + Ite( + Neq(Mod(a, b), Int(0)), // if (a mod b != 0) + Ite( + Geq(b, Int(0)), // if (b >= 0) + Add(aDivB, Int(1)), // a div b + 1 // else - Sub(aDivB, Int(1)) // a div b - 1 - ), // else - aDivB // a div b - ) - ); + Sub(aDivB, Int(1)) // a div b - 1 + ), // else + aDivB // a div b + )); } /** @@ -403,25 +510,30 @@ private Expr createIntDiv(Expr a, Expr b) { */ private Expr createIntMod(Expr a, Expr b) { ModExpr aModB = Mod(a, b); - return Ite(Eq(aModB, Int(0)), aModB, - Ite(Geq(a, Int(0)), // if (a >= 0) - aModB, // a mod b + return Ite( + Eq(aModB, Int(0)), + aModB, + Ite( + Geq(a, Int(0)), // if (a >= 0) + aModB, // a mod b // else - Ite(Geq(b, Int(0)), // if (b >= 0) - Sub(aModB, b), // a mod b - b - Add(aModB, b) // a mod b + b - ) - )); + Ite( + Geq(b, Int(0)), // if (b >= 0) + Sub(aModB, b), // a mod b - b + Add(aModB, b) // a mod b + b + ))); } @Override - public Expr visitCastExpressionUnaryExpression(CParser.CastExpressionUnaryExpressionContext ctx) { + public Expr visitCastExpressionUnaryExpression( + CParser.CastExpressionUnaryExpressionContext ctx) { return ctx.unaryExpression().accept(this); } @Override public Expr visitCastExpressionCast(CParser.CastExpressionCastContext ctx) { - CComplexType actualType = ctx.castDeclarationSpecifierList().accept(typeVisitor).getActualType(); + CComplexType actualType = + ctx.castDeclarationSpecifierList().accept(typeVisitor).getActualType(); Expr expr = actualType.castTo(ctx.castExpression().accept(this)); parseContext.getMetadata().create(expr, "cType", actualType); expr = actualType.castTo(expr); @@ -430,24 +542,51 @@ public Expr visitCastExpressionCast(CParser.CastExpressionCastContext ctx) { } @Override - public Expr visitUnaryExpressionSizeOrAlignOf(CParser.UnaryExpressionSizeOrAlignOfContext ctx) { + public Expr visitUnaryExpressionSizeOrAlignOf( + CParser.UnaryExpressionSizeOrAlignOfContext ctx) { if (ctx.Alignof() != null) { - uniqueWarningLogger.write(Level.INFO, "WARNING: alignof is not yet implemented, using a literal 0 instead.\n"); + uniqueWarningLogger.write( + Level.INFO, + "WARNING: alignof is not yet implemented, using a literal 0 instead.\n"); CComplexType signedInt = CComplexType.getSignedInt(parseContext); LitExpr zero = signedInt.getNullValue(); parseContext.getMetadata().create(zero, "cType", signedInt); return zero; } else if (ctx.typeName() != null) { - final Optional type = typedefVisitor.getType(ctx.typeName().getText()) - .or(() -> Optional.ofNullable(CComplexType.getType(ctx.typeName().getText(), parseContext))) - .or(() -> parseContext.getMetadata().getMetadataValue(ctx.typeName().getText(), "cTypedefName").map(it -> (CComplexType) it)) - .or(() -> Optional.ofNullable(CComplexType.getType(getVar(ctx.typeName().getText()).getRef(), parseContext))); + final Optional type = + typedefVisitor + .getType(ctx.typeName().getText()) + .or( + () -> + Optional.ofNullable( + CComplexType.getType( + ctx.typeName().getText(), + parseContext))) + .or( + () -> + parseContext + .getMetadata() + .getMetadataValue( + ctx.typeName().getText(), + "cTypedefName") + .map(it -> (CComplexType) it)) + .or( + () -> + Optional.ofNullable( + CComplexType.getType( + getVar(ctx.typeName().getText()) + .getRef(), + parseContext))); if (type.isPresent()) { - LitExpr value = CComplexType.getSignedInt(parseContext).getValue("" + type.get().width() / 8); + LitExpr value = + CComplexType.getSignedInt(parseContext) + .getValue("" + type.get().width() / 8); return value; } else { - uniqueWarningLogger.write(Level.INFO, "WARNING: sizeof got unknown type, using a literal 0 instead.\n"); + uniqueWarningLogger.write( + Level.INFO, + "WARNING: sizeof got unknown type, using a literal 0 instead.\n"); CComplexType signedInt = CComplexType.getSignedInt(parseContext); LitExpr zero = signedInt.getNullValue(); parseContext.getMetadata().create(zero, "cType", signedInt); @@ -456,10 +595,10 @@ public Expr visitUnaryExpressionSizeOrAlignOf(CParser.UnaryExpressionSizeOrAl } else { // expr != null final var expr = ctx.expression().accept(this); final var type = CComplexType.getType(expr, parseContext); - LitExpr value = CComplexType.getSignedInt(parseContext).getValue("" + type.width() / 8); + LitExpr value = + CComplexType.getSignedInt(parseContext).getValue("" + type.width() / 8); return value; } - } @Override @@ -468,17 +607,19 @@ public Expr visitUnaryExpression(CParser.UnaryExpressionContext ctx) { if (ctx.unaryExpressionSizeOrAlignOf() != null) { ret = ctx.unaryExpressionSizeOrAlignOf().accept(this); } else { - ret = ctx.unaryExpressionCast() == null ? ctx.postfixExpression().accept(this) : ctx.unaryExpressionCast().accept(this); + ret = + ctx.unaryExpressionCast() == null + ? ctx.postfixExpression().accept(this) + : ctx.unaryExpressionCast().accept(this); } - int increment = ctx.unaryExpressionIncrement().size() - ctx.unaryExpressionDecrement().size(); + int increment = + ctx.unaryExpressionIncrement().size() - ctx.unaryExpressionDecrement().size(); if (increment != 0) { Expr expr = ret; CComplexType type = CComplexType.getType(ret, parseContext); for (int i = 0; i < Math.abs(increment); i++) { - if (increment < 0) - expr = AbstractExprs.Sub(expr, type.getUnitValue()); - else - expr = AbstractExprs.Add(expr, type.getUnitValue()); + if (increment < 0) expr = AbstractExprs.Sub(expr, type.getUnitValue()); + else expr = AbstractExprs.Add(expr, type.getUnitValue()); } parseContext.getMetadata().create(expr, "cType", type); expr = type.castTo(expr); @@ -498,24 +639,32 @@ public Expr visitUnaryExpressionCast(CParser.UnaryExpressionCastContext ctx) Expr accept = ctx.castExpression().accept(this); CComplexType type; switch (ctx.unaryOperator().getText()) { - case "-": { - Expr negExpr = AbstractExprs.Neg(accept); - type = CComplexType.getType(accept, parseContext); - parseContext.getMetadata().create(negExpr, "cType", type); - negExpr = type.castTo(negExpr); - parseContext.getMetadata().create(negExpr, "cType", type); - return negExpr; - } + case "-": + { + Expr negExpr = AbstractExprs.Neg(accept); + type = CComplexType.getType(accept, parseContext); + parseContext.getMetadata().create(negExpr, "cType", type); + negExpr = type.castTo(negExpr); + parseContext.getMetadata().create(negExpr, "cType", type); + return negExpr; + } case "+": return accept; // no need to update type, it remains the same case "!": CComplexType signedInt = CComplexType.getSignedInt(parseContext); - accept = Ite(Eq(accept, CComplexType.getType(accept, parseContext).getNullValue()), signedInt.getUnitValue(), signedInt.getNullValue()); + accept = + Ite( + Eq( + accept, + CComplexType.getType(accept, parseContext).getNullValue()), + signedInt.getUnitValue(), + signedInt.getNullValue()); parseContext.getMetadata().create(accept, "cType", signedInt); return accept; case "~": type = CComplexType.getType(accept, parseContext); - CComplexType smallestCommonType = CComplexType.getSmallestCommonType(List.of(type), parseContext); + CComplexType smallestCommonType = + CComplexType.getSmallestCommonType(List.of(type), parseContext); checkState(accept.getType() instanceof BvType); accept = smallestCommonType.castTo(accept); //noinspection unchecked @@ -526,27 +675,34 @@ public Expr visitUnaryExpressionCast(CParser.UnaryExpressionCastContext ctx) return expr; case "&": final Expr localAccept = accept; - checkState(localAccept instanceof RefExpr && ((RefExpr) localAccept).getDecl() instanceof VarDecl, "Referencing non-variable expressions is not allowed!"); + checkState( + localAccept instanceof RefExpr + && ((RefExpr) localAccept).getDecl() instanceof VarDecl, + "Referencing non-variable expressions is not allowed!"); return reference((RefExpr) localAccept); case "*": type = CComplexType.getType(accept, parseContext); if (type instanceof CPointer) type = ((CPointer) type).getEmbeddedType(); else if (type instanceof CArray) type = ((CArray) type).getEmbeddedType(); - return dereference(accept, CComplexType.getUnsignedLong(parseContext).getNullValue(), type); + return dereference( + accept, CComplexType.getUnsignedLong(parseContext).getNullValue(), type); } return accept; } @SuppressWarnings("unchecked") - private Expr dereference(Expr accept, Expr offset, CComplexType type) { + private Expr dereference( + Expr accept, Expr offset, CComplexType type) { CComplexType ptrType = CComplexType.getType(accept, parseContext); - Dereference of = Exprs.Dereference((Expr) accept, ptrType.castTo(offset), type.getSmtType()); + Dereference of = + Exprs.Dereference((Expr) accept, ptrType.castTo(offset), type.getSmtType()); parseContext.getMetadata().create(of, "cType", type); return of; } private Expr reference(RefExpr accept) { - final var newType = new CPointer(null, CComplexType.getType(accept, parseContext), parseContext); + final var newType = + new CPointer(null, CComplexType.getType(accept, parseContext), parseContext); Reference of = Reference(accept, newType.getSmtType()); parseContext.getMetadata().create(of, "cType", newType); return of; @@ -571,7 +727,10 @@ public Expr visitPostfixExpressionBrackets(CParser.PostfixExpressionBracketsC @Override public Expr visitGccPrettyFunc(CParser.GccPrettyFuncContext ctx) { - uniqueWarningLogger.write(Level.INFO, "WARNING: gcc intrinsic encountered in place of an expression, using a literal 0 instead.\n"); + uniqueWarningLogger.write( + Level.INFO, + "WARNING: gcc intrinsic encountered in place of an expression, using a literal 0" + + " instead.\n"); CComplexType signedInt = CComplexType.getSignedInt(parseContext); LitExpr zero = signedInt.getNullValue(); parseContext.getMetadata().create(zero, "cType", signedInt); @@ -610,13 +769,16 @@ public Expr visitPrimaryExpressionConstant(CParser.PrimaryExpressionConstantC BigFloat bigFloat; if (text.startsWith("0x")) { - throw new UnsupportedFrontendElementException("Hexadecimal FP constants are not yet supported!"); + throw new UnsupportedFrontendElementException( + "Hexadecimal FP constants are not yet supported!"); } else if (text.startsWith("0b")) { - throw new UnsupportedFrontendElementException("Binary FP constants are not yet supported!"); + throw new UnsupportedFrontendElementException( + "Binary FP constants are not yet supported!"); } else { bigFloat = new BigFloat(text, new BinaryMathContext(significand - 1, exponent)); } - FpLitExpr fpLitExpr = FpUtils.bigFloatToFpLitExpr(bigFloat, FpType(exponent, significand)); + FpLitExpr fpLitExpr = + FpUtils.bigFloatToFpLitExpr(bigFloat, FpType(exponent, significand)); parseContext.getMetadata().create(fpLitExpr, "cType", type); return fpLitExpr; @@ -638,6 +800,10 @@ public Expr visitPrimaryExpressionConstant(CParser.PrimaryExpressionConstantC bigInteger = new BigInteger(text.substring(3, text.length() - 1), 8); } else if (text.startsWith("'\\")) { // char c = '\0' bigInteger = new BigInteger(text.substring(2, text.length() - 1), 10); + } else if (text.startsWith("'") + && text.endsWith("'") + && text.length() == 3) { // char c = 'X' + bigInteger = BigInteger.valueOf((int) text.charAt(1)); } else { bigInteger = new BigInteger(text, 10); } @@ -650,27 +816,30 @@ public Expr visitPrimaryExpressionConstant(CParser.PrimaryExpressionConstantC else if (isUnsigned) type = CComplexType.getUnsignedInt(parseContext); else type = CComplexType.getSignedInt(parseContext); - LitExpr litExpr = parseContext.getArithmetic() == ArchitectureConfig.ArithmeticType.bitvector ? - isUnsigned ? - BvUtils.bigIntegerToUnsignedBvLitExpr(bigInteger, type.width()) : - BvUtils.bigIntegerToSignedBvLitExpr(bigInteger, type.width()) : - Int(bigInteger); + LitExpr litExpr = + parseContext.getArithmetic() == ArchitectureConfig.ArithmeticType.bitvector + ? isUnsigned + ? BvUtils.bigIntegerToUnsignedBvLitExpr( + bigInteger, type.width()) + : BvUtils.bigIntegerToSignedBvLitExpr(bigInteger, type.width()) + : Int(bigInteger); parseContext.getMetadata().create(litExpr, "cType", type); return litExpr; } - } @Override - public Expr visitPrimaryExpressionBraceExpression(CParser.PrimaryExpressionBraceExpressionContext ctx) { + public Expr visitPrimaryExpressionBraceExpression( + CParser.PrimaryExpressionBraceExpressionContext ctx) { CStatement statement = ctx.expression().accept(functionVisitor); preStatements.add(statement); return statement.getExpression(); } @Override - public Expr visitPrimaryExpressionGccExtension(CParser.PrimaryExpressionGccExtensionContext ctx) { + public Expr visitPrimaryExpressionGccExtension( + CParser.PrimaryExpressionGccExtensionContext ctx) { return null; } @@ -685,7 +854,8 @@ public Expr visitPrimaryExpressionStrings(CParser.PrimaryExpressionStringsCon class PostfixVisitor extends CBaseVisitor, Expr>> { @Override - public Function, Expr> visitPostfixExpressionBrackets(PostfixExpressionBracketsContext ctx) { + public Function, Expr> visitPostfixExpressionBrackets( + PostfixExpressionBracketsContext ctx) { return (primary) -> { CComplexType arrayType = CComplexType.getType(primary, parseContext); if (arrayType instanceof CArray) { @@ -711,14 +881,23 @@ public Function, Expr> visitPostfixExpressionBrackets(PostfixExpressi } @Override - public Function, Expr> visitPostfixExpressionBraces(PostfixExpressionBracesContext ctx) { + public Function, Expr> visitPostfixExpressionBraces( + PostfixExpressionBracesContext ctx) { return (expr) -> { checkState(expr instanceof RefExpr, "Only variables are callable now."); CParser.ArgumentExpressionListContext exprList = ctx.argumentExpressionList(); - List arguments = exprList == null ? List.of() : exprList.assignmentExpression().stream().map(assignmentExpressionContext -> assignmentExpressionContext.accept(functionVisitor)).collect(Collectors.toList()); - CCall cCall = new CCall(((RefExpr) expr).getDecl().getName(), arguments, parseContext); - if (cCall.getFunctionId().contains("pthread")) - parseContext.setMultiThreading(true); + List arguments = + exprList == null + ? List.of() + : exprList.assignmentExpression().stream() + .map( + assignmentExpressionContext -> + assignmentExpressionContext.accept( + functionVisitor)) + .collect(Collectors.toList()); + CCall cCall = + new CCall(((RefExpr) expr).getDecl().getName(), arguments, parseContext); + if (cCall.getFunctionId().contains("pthread")) parseContext.setMultiThreading(true); preStatements.add(cCall); functionVisitor.recordMetadata(ctx, cCall); return cCall.getRet().getRef(); @@ -726,43 +905,69 @@ public Function, Expr> visitPostfixExpressionBraces(PostfixExpression } @Override - public Function, Expr> visitPostfixExpressionMemberAccess(PostfixExpressionMemberAccessContext ctx) { + public Function, Expr> visitPostfixExpressionMemberAccess( + PostfixExpressionMemberAccessContext ctx) { return (primary) -> { final CComplexType type = CComplexType.getType(primary, parseContext); checkState(type instanceof CStruct, "Only structs expected here"); final CStruct structType = (CStruct) type; final String accName = ctx.Identifier().getText(); - final int index = structType.getFields().stream().map(Tuple2::get1).toList().indexOf(accName); + final int index = + structType.getFields().stream().map(Tuple2::get1).toList().indexOf(accName); final var idxExpr = type.getValue(String.valueOf(index)); final var embeddedType = structType.getFieldsAsMap().get(accName); - checkState(embeddedType != null, "Field [%s] not found, available fields are: %s".formatted(accName, ((CStruct) type).getFieldsAsMap().keySet())); - primary = Exprs.Dereference(cast(primary, primary.getType()), cast(idxExpr, primary.getType()), embeddedType.getSmtType()); + checkState( + embeddedType != null, + "Field [%s] not found, available fields are: %s" + .formatted(accName, ((CStruct) type).getFieldsAsMap().keySet())); + primary = + Exprs.Dereference( + cast(primary, primary.getType()), + cast(idxExpr, primary.getType()), + embeddedType.getSmtType()); parseContext.getMetadata().create(primary, "cType", embeddedType); return primary; }; } @Override - public Function, Expr> visitPostfixExpressionPtrMemberAccess(PostfixExpressionPtrMemberAccessContext ctx) { + public Function, Expr> visitPostfixExpressionPtrMemberAccess( + PostfixExpressionPtrMemberAccessContext ctx) { return (primary) -> { final CComplexType type = CComplexType.getType(primary, parseContext); - checkState(type instanceof CPointer || type instanceof CArray, "Only pointers expected here"); - final CComplexType structTypeErased = type instanceof CPointer ? ((CPointer) type).getEmbeddedType() : ((CArray) type).getEmbeddedType(); + checkState( + type instanceof CPointer || type instanceof CArray, + "Only pointers expected here"); + final CComplexType structTypeErased = + type instanceof CPointer + ? ((CPointer) type).getEmbeddedType() + : ((CArray) type).getEmbeddedType(); checkState(structTypeErased instanceof CStruct, "Only structs expected here"); final CStruct structType = (CStruct) structTypeErased; final String accName = ctx.Identifier().getText(); - final int index = structType.getFields().stream().map(Tuple2::get1).toList().indexOf(accName); + final int index = + structType.getFields().stream().map(Tuple2::get1).toList().indexOf(accName); final var idxExpr = structTypeErased.getValue(String.valueOf(index)); final var embeddedType = structType.getFieldsAsMap().get(accName); - checkState(embeddedType != null, "Field [%s] not found, available fields are: %s".formatted(accName, ((CStruct) structTypeErased).getFieldsAsMap().keySet())); - primary = Exprs.Dereference(cast(primary, primary.getType()), cast(idxExpr, primary.getType()), embeddedType.getSmtType()); + checkState( + embeddedType != null, + "Field [%s] not found, available fields are: %s" + .formatted( + accName, + ((CStruct) structTypeErased).getFieldsAsMap().keySet())); + primary = + Exprs.Dereference( + cast(primary, primary.getType()), + cast(idxExpr, primary.getType()), + embeddedType.getSmtType()); parseContext.getMetadata().create(primary, "cType", embeddedType); return primary; }; } @Override - public Function, Expr> visitPostfixExpressionIncrement(PostfixExpressionIncrementContext ctx) { + public Function, Expr> visitPostfixExpressionIncrement( + PostfixExpressionIncrementContext ctx) { return (primary) -> { CComplexType type = CComplexType.getType(primary, parseContext); Expr expr = primary; @@ -779,11 +984,11 @@ public Function, Expr> visitPostfixExpressionIncrement(PostfixExpress functionVisitor.recordMetadata(ctx, cexpr); return primary; }; - } @Override - public Function, Expr> visitPostfixExpressionDecrement(PostfixExpressionDecrementContext ctx) { + public Function, Expr> visitPostfixExpressionDecrement( + PostfixExpressionDecrementContext ctx) { return (primary) -> { CComplexType type = CComplexType.getType(primary, parseContext); Expr expr = primary; diff --git a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/DefaultGraphPatternCompiler.kt b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/DefaultGraphPatternCompiler.kt index facf177953..aec2b261b9 100644 --- a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/DefaultGraphPatternCompiler.kt +++ b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/DefaultGraphPatternCompiler.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.graphsolver.compilers import hu.bme.mit.theta.common.Tuple @@ -24,94 +23,132 @@ import hu.bme.mit.theta.graphsolver.patterns.patterns.* abstract class DefaultGraphPatternCompiler : GraphPatternCompiler { - override fun addEvents(events: List) {} - override fun addFacts(edges: Map, ThreeVL>) {} - override fun compile(acyclic: Acyclic): T? = acyclic.constrainedRule.accept(this) - override fun compile(cyclic: Cyclic): T? = cyclic.constrainedRule.accept(this) - override fun compile(empty: Empty): T? = empty.constrainedRule.accept(this) - override fun compile(nonempty: Nonempty): T? = nonempty.constrainedRule.accept(this) - override fun compile(reflexive: Reflexive): T? = reflexive.constrainedRule.accept(this) - override fun compile(irreflexive: Irreflexive): T? = irreflexive.constrainedRule.accept(this) - - override fun compile(pattern: CartesianProduct): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: Complement): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: ComplementNode): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Difference): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: DifferenceNode): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: Domain): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: EmptySet): T? = null - override fun compile(pattern: EmptyRel): T? = null - override fun compile(pattern: IdentityClosure): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Intersection): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: IntersectionNode): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: Inverse): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Range): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: ReflexiveTransitiveClosure): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Self): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Sequence): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: Toid): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: TransitiveClosure): T? { - pattern.op.accept(this); return null - } - - override fun compile(pattern: Union): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: UnionNode): T? { - pattern.op1.accept(this); pattern.op2.accept(this); return null - } - - override fun compile(pattern: BasicEventSet): T? = null - override fun compile(pattern: BasicRelation): T? = null - - override fun getCompleteGraph(mcm: Set, - model: Valuation): Pair, Map, ThreeVL>> { - error("Not implemented") - } -} \ No newline at end of file + override fun addEvents(events: List) {} + + override fun addFacts(edges: Map, ThreeVL>) {} + + override fun compile(acyclic: Acyclic): T? = acyclic.constrainedRule.accept(this) + + override fun compile(cyclic: Cyclic): T? = cyclic.constrainedRule.accept(this) + + override fun compile(empty: Empty): T? = empty.constrainedRule.accept(this) + + override fun compile(nonempty: Nonempty): T? = nonempty.constrainedRule.accept(this) + + override fun compile(reflexive: Reflexive): T? = reflexive.constrainedRule.accept(this) + + override fun compile(irreflexive: Irreflexive): T? = irreflexive.constrainedRule.accept(this) + + override fun compile(pattern: CartesianProduct): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: Complement): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: ComplementNode): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Difference): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: DifferenceNode): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: Domain): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: EmptySet): T? = null + + override fun compile(pattern: EmptyRel): T? = null + + override fun compile(pattern: IdentityClosure): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Intersection): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: IntersectionNode): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: Inverse): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Range): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: ReflexiveTransitiveClosure): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Self): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Sequence): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: Toid): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: TransitiveClosure): T? { + pattern.op.accept(this) + return null + } + + override fun compile(pattern: Union): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: UnionNode): T? { + pattern.op1.accept(this) + pattern.op2.accept(this) + return null + } + + override fun compile(pattern: BasicEventSet): T? = null + + override fun compile(pattern: BasicRelation): T? = null + + override fun getCompleteGraph( + mcm: Set, + model: Valuation, + ): Pair, Map, ThreeVL>> { + error("Not implemented") + } +} diff --git a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/GraphPatternCompiler.kt b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/GraphPatternCompiler.kt index 0490c9f006..8a7e2045b0 100644 --- a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/GraphPatternCompiler.kt +++ b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/GraphPatternCompiler.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.graphsolver.compilers import hu.bme.mit.theta.common.Tuple @@ -28,37 +27,68 @@ import hu.bme.mit.theta.graphsolver.patterns.patterns.* */ interface GraphPatternCompiler { - fun addEvents(events: List) - fun addFacts(edges: Map, ThreeVL>) - fun compile(acyclic: Acyclic): T1 - fun compile(cyclic: Cyclic): T1 - fun compile(empty: Empty): T1 - fun compile(nonempty: Nonempty): T1 - fun compile(reflexive: Reflexive): T1 - fun compile(irreflexive: Irreflexive): T1 - - fun compile(pattern: CartesianProduct): T2 - fun compile(pattern: Complement): T2 - fun compile(pattern: ComplementNode): T2 - fun compile(pattern: Difference): T2 - fun compile(pattern: DifferenceNode): T2 - fun compile(pattern: Domain): T2 - fun compile(pattern: EmptySet): T2 - fun compile(pattern: EmptyRel): T2 - fun compile(pattern: IdentityClosure): T2 - fun compile(pattern: Intersection): T2 - fun compile(pattern: IntersectionNode): T2 - fun compile(pattern: Inverse): T2 - fun compile(pattern: Range): T2 - fun compile(pattern: ReflexiveTransitiveClosure): T2 - fun compile(pattern: Self): T2 - fun compile(pattern: Sequence): T2 - fun compile(pattern: Toid): T2 - fun compile(pattern: TransitiveClosure): T2 - fun compile(pattern: Union): T2 - fun compile(pattern: UnionNode): T2 - fun compile(pattern: BasicEventSet): T2 - fun compile(pattern: BasicRelation): T2 - fun getCompleteGraph(namedPatterns: Set, - model: Valuation): Pair, Map, ThreeVL>> -} \ No newline at end of file + fun addEvents(events: List) + + fun addFacts(edges: Map, ThreeVL>) + + fun compile(acyclic: Acyclic): T1 + + fun compile(cyclic: Cyclic): T1 + + fun compile(empty: Empty): T1 + + fun compile(nonempty: Nonempty): T1 + + fun compile(reflexive: Reflexive): T1 + + fun compile(irreflexive: Irreflexive): T1 + + fun compile(pattern: CartesianProduct): T2 + + fun compile(pattern: Complement): T2 + + fun compile(pattern: ComplementNode): T2 + + fun compile(pattern: Difference): T2 + + fun compile(pattern: DifferenceNode): T2 + + fun compile(pattern: Domain): T2 + + fun compile(pattern: EmptySet): T2 + + fun compile(pattern: EmptyRel): T2 + + fun compile(pattern: IdentityClosure): T2 + + fun compile(pattern: Intersection): T2 + + fun compile(pattern: IntersectionNode): T2 + + fun compile(pattern: Inverse): T2 + + fun compile(pattern: Range): T2 + + fun compile(pattern: ReflexiveTransitiveClosure): T2 + + fun compile(pattern: Self): T2 + + fun compile(pattern: Sequence): T2 + + fun compile(pattern: Toid): T2 + + fun compile(pattern: TransitiveClosure): T2 + + fun compile(pattern: Union): T2 + + fun compile(pattern: UnionNode): T2 + + fun compile(pattern: BasicEventSet): T2 + + fun compile(pattern: BasicRelation): T2 + + fun getCompleteGraph( + namedPatterns: Set, + model: Valuation, + ): Pair, Map, ThreeVL>> +} diff --git a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/pattern2expr/Pattern2ExprCompiler.kt b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/pattern2expr/Pattern2ExprCompiler.kt index 4102e138ff..9ef99e4e24 100644 --- a/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/pattern2expr/Pattern2ExprCompiler.kt +++ b/subprojects/solver/graph-solver/src/main/java/hu/bme/mit/theta/graphsolver/compilers/pattern2expr/Pattern2ExprCompiler.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.graphsolver.compilers.pattern2expr import hu.bme.mit.theta.common.Tuple @@ -34,313 +33,423 @@ import kotlin.jvm.optionals.getOrNull class Pattern2ExprCompiler : GraphPatternCompiler, Map>> { - private val events = ArrayList() - private val facts = LinkedHashMap, ThreeVL>() - private val namedLookup = LinkedHashMap, ConstDecl>() - - private val transitiveConstraints = ArrayList>() - override fun addEvents(events: List) { - this.events.addAll(events) - } - - override fun addFacts(edges: Map, ThreeVL>) { - this.facts.putAll(edges) - } - - @OptIn(ExperimentalStdlibApi::class) - override fun getCompleteGraph(namedPatterns: Set, - model: Valuation): Pair, Map, ThreeVL>> { - val ret = LinkedHashMap, ThreeVL>() - ret.putAll(facts) - ret.putAll(namedLookup.map { n -> - model.eval(n.value).map { Pair(n.key, if (it == True()) ThreeVL.TRUE else ThreeVL.FALSE) }.getOrNull() - }.filterNotNull().toMap()) - ret.putAll(namedPatterns.map { pattern -> - pattern.accept(this).map { (tup, expr) -> - Pair(Pair(pattern.patternName!!, tup), if (expr.eval(model) == True()) ThreeVL.TRUE else ThreeVL.FALSE) - } - }.flatten().toMap()) - return Pair(events, ret) - } - - override fun compile(acyclic: Acyclic): Expr = - Irreflexive(TransitiveClosure(acyclic.constrainedRule)).accept(this) - - override fun compile(cyclic: Cyclic): Expr = - Reflexive(TransitiveClosure(cyclic.constrainedRule)).accept(this) - - override fun compile(empty: Empty): Expr { - val compiledRule = empty.constrainedRule.accept(this) - val ret = And(Not(Or(compiledRule.values)), And(transitiveConstraints)) - transitiveConstraints.clear() - return ret - } - - override fun compile(nonempty: Nonempty): Expr { - val compiledRule = nonempty.constrainedRule.accept(this) - val ret = And(Or(compiledRule.values), And(transitiveConstraints)) - transitiveConstraints.clear() - return ret - } - - override fun compile(reflexive: Reflexive): Expr { - val compiled = reflexive.constrainedRule.accept(this) - val ret = And(Or(events.map { compiled[Tuple2.of(it, it)] }), And(transitiveConstraints)) - transitiveConstraints.clear() - return ret - } - - override fun compile(irreflexive: Irreflexive): Expr { - val compiled = irreflexive.constrainedRule.accept(this) - val ret = And(Not(Or(events.map { compiled[Tuple2.of(it, it)] })), And(transitiveConstraints)) - transitiveConstraints.clear() - return ret - } - - override fun compile(pattern: CartesianProduct): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(b)])) - } + private val events = ArrayList() + private val facts = LinkedHashMap, ThreeVL>() + private val namedLookup = LinkedHashMap, ConstDecl>() + + private val transitiveConstraints = ArrayList>() + + override fun addEvents(events: List) { + this.events.addAll(events) + } + + override fun addFacts(edges: Map, ThreeVL>) { + this.facts.putAll(edges) + } + + @OptIn(ExperimentalStdlibApi::class) + override fun getCompleteGraph( + namedPatterns: Set, + model: Valuation, + ): Pair, Map, ThreeVL>> { + val ret = LinkedHashMap, ThreeVL>() + ret.putAll(facts) + ret.putAll( + namedLookup + .map { n -> + model + .eval(n.value) + .map { Pair(n.key, if (it == True()) ThreeVL.TRUE else ThreeVL.FALSE) } + .getOrNull() } - return ret.flatten().toMap() - } - - override fun compile(pattern: Complement): Map> = - pattern.op.accept(this).map { Pair(it.key, Not(it.value)) }.toMap() - - override fun compile(pattern: ComplementNode): Map> = - pattern.op.accept(this).map { Pair(it.key, Not(it.value)) }.toMap() - - override fun compile(pattern: Difference): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), And(op1Compiled[Tuple2.of(a, b)], Not(op2Compiled[Tuple2.of(a, b)]))) - } + .filterNotNull() + .toMap() + ) + ret.putAll( + namedPatterns + .map { pattern -> + pattern.accept(this).map { (tup, expr) -> + Pair( + Pair(pattern.patternName!!, tup), + if (expr.eval(model) == True()) ThreeVL.TRUE else ThreeVL.FALSE, + ) + } } - return ret.flatten().toMap() - } - - override fun compile(pattern: DifferenceNode): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], Not(op2Compiled[Tuple1.of(a)]))) } - return ret.toMap() - } - - override fun compile(pattern: Domain): Map> { - val opCompiled = pattern.op.accept(this) - - val ret = events.map { a -> Pair(Tuple1.of(a), Or(events.map { b -> opCompiled[Tuple2.of(a, b)] })) } - return ret.toMap() - } - - override fun compile(pattern: EmptyRel): Map> { - return events.map { a -> events.map { b -> Pair(Tuple2.of(a, b), False()) } }.flatten().toMap() - } - - override fun compile(pattern: EmptySet): Map> { - return events.associate { a -> Pair(Tuple1.of(a), False()) } - } - - override fun compile(pattern: IdentityClosure): Map> { - val opCompiled = pattern.op.accept(this) - return events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), if (a == b) True() else checkNotNull(opCompiled[Tuple2.of(a, b)])) - } - }.flatten().toMap() - } - - override fun compile(pattern: Intersection): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), And(op1Compiled[Tuple2.of(a, b)], op2Compiled[Tuple2.of(a, b)])) - } + .flatten() + .toMap() + ) + return Pair(events, ret) + } + + override fun compile(acyclic: Acyclic): Expr = + Irreflexive(TransitiveClosure(acyclic.constrainedRule)).accept(this) + + override fun compile(cyclic: Cyclic): Expr = + Reflexive(TransitiveClosure(cyclic.constrainedRule)).accept(this) + + override fun compile(empty: Empty): Expr { + val compiledRule = empty.constrainedRule.accept(this) + val ret = And(Not(Or(compiledRule.values)), And(transitiveConstraints)) + transitiveConstraints.clear() + return ret + } + + override fun compile(nonempty: Nonempty): Expr { + val compiledRule = nonempty.constrainedRule.accept(this) + val ret = And(Or(compiledRule.values), And(transitiveConstraints)) + transitiveConstraints.clear() + return ret + } + + override fun compile(reflexive: Reflexive): Expr { + val compiled = reflexive.constrainedRule.accept(this) + val ret = And(Or(events.map { compiled[Tuple2.of(it, it)] }), And(transitiveConstraints)) + transitiveConstraints.clear() + return ret + } + + override fun compile(irreflexive: Irreflexive): Expr { + val compiled = irreflexive.constrainedRule.accept(this) + val ret = And(Not(Or(events.map { compiled[Tuple2.of(it, it)] })), And(transitiveConstraints)) + transitiveConstraints.clear() + return ret + } + + override fun compile(pattern: CartesianProduct): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(b)])) } - return ret.flatten().toMap() - } - - override fun compile(pattern: IntersectionNode): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(a)])) } - return ret.toMap() - } - - override fun compile(pattern: Inverse): Map> { - val opCompiled = pattern.op.accept(this) - return events.map { a -> events.map { b -> Pair(Tuple2.of(a, b), checkNotNull(opCompiled[Tuple2.of(b, a)])) } } - .flatten() - .toMap() - } - - override fun compile(pattern: Range): Map> { - val opCompiled = pattern.op.accept(this) - - val ret = events.map { a -> Pair(Tuple1.of(a), Or(events.map { b -> opCompiled[Tuple2.of(a, b)] })) } - return ret.toMap() - } - - override fun compile(pattern: ReflexiveTransitiveClosure): Map> { - val opCompiled = pattern.op.accept(this) - val uuid = Random().nextInt() - val consts = events.map { a -> - events.map { b -> - val const = if (pattern.patternName != null) { - val const = namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] ?: Const( - "RTC_" + uuid + "_" + a + "_" + b, Bool()) - namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] = const - const - } else { - Const("RTC_" + uuid + "_" + a + "_" + b, Bool()) - } - Pair(Tuple2.of(a, b), const) - } - }.flatten().toMap() - - val constraints = And(events.map { a -> - events.map { b -> - Iff(Or(opCompiled[Tuple2.of(a, b)], Or(events.map { c -> - Or( - And(opCompiled[Tuple2.of(a, c)], checkNotNull(consts[Tuple2.of(c, b)]).ref), - And(checkNotNull(consts[Tuple2.of(a, c)]).ref, opCompiled[Tuple2.of(c, b)]) - ) - })), checkNotNull(consts[Tuple2.of(a, b)]).ref) - } - }.flatten()) - transitiveConstraints.add(constraints) - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), if (a == b) True() else checkNotNull(consts[Tuple2.of(a, b)]).ref) - } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: Complement): Map> = + pattern.op.accept(this).map { Pair(it.key, Not(it.value)) }.toMap() + + override fun compile(pattern: ComplementNode): Map> = + pattern.op.accept(this).map { Pair(it.key, Not(it.value)) }.toMap() + + override fun compile(pattern: Difference): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + events.map { b -> + Pair( + Tuple2.of(a, b), + And(op1Compiled[Tuple2.of(a, b)], Not(op2Compiled[Tuple2.of(a, b)])), + ) } - return ret.flatten().toMap() - } - - override fun compile(pattern: Self): Map> = - pattern.op.accept(this) - - override fun compile(pattern: Sequence): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), Or(events.filter { c -> a != c && b != c }.map { c -> - And(op1Compiled[Tuple2.of(a, c)], op2Compiled[Tuple2.of(c, b)]) - })) - } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: DifferenceNode): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], Not(op2Compiled[Tuple1.of(a)]))) + } + return ret.toMap() + } + + override fun compile(pattern: Domain): Map> { + val opCompiled = pattern.op.accept(this) + + val ret = + events.map { a -> Pair(Tuple1.of(a), Or(events.map { b -> opCompiled[Tuple2.of(a, b)] })) } + return ret.toMap() + } + + override fun compile(pattern: EmptyRel): Map> { + return events.map { a -> events.map { b -> Pair(Tuple2.of(a, b), False()) } }.flatten().toMap() + } + + override fun compile(pattern: EmptySet): Map> { + return events.associate { a -> Pair(Tuple1.of(a), False()) } + } + + override fun compile(pattern: IdentityClosure): Map> { + val opCompiled = pattern.op.accept(this) + return events + .map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), if (a == b) True() else checkNotNull(opCompiled[Tuple2.of(a, b)])) } - return ret.flatten().toMap() - } - - override fun compile(pattern: Toid): Map> { - val opCompiled = pattern.op.accept(this) - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), if (a == b) checkNotNull(opCompiled[Tuple1.of(a)]) else False()) - } + } + .flatten() + .toMap() + } + + override fun compile(pattern: Intersection): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), And(op1Compiled[Tuple2.of(a, b)], op2Compiled[Tuple2.of(a, b)])) } - return ret.flatten().toMap() - } - - override fun compile(pattern: TransitiveClosure): Map> { - val uuid = Random().nextInt() - val opCompiled = pattern.op.accept(this) - val consts = events.map { a -> - events.map { b -> - val const = if (pattern.patternName != null) { - val const = namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] ?: Const( - "TC_" + uuid + "_" + a + "_" + b, Bool()) - namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] = const - const - } else { - Const("TC_" + uuid + "_" + a + "_" + b, Bool()) - } - Pair(Tuple2.of(a, b), const) - } - }.flatten().toMap() + } + return ret.flatten().toMap() + } + + override fun compile(pattern: IntersectionNode): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(a)])) + } + return ret.toMap() + } + + override fun compile(pattern: Inverse): Map> { + val opCompiled = pattern.op.accept(this) + return events + .map { a -> + events.map { b -> Pair(Tuple2.of(a, b), checkNotNull(opCompiled[Tuple2.of(b, a)])) } + } + .flatten() + .toMap() + } + + override fun compile(pattern: Range): Map> { + val opCompiled = pattern.op.accept(this) + + val ret = + events.map { a -> Pair(Tuple1.of(a), Or(events.map { b -> opCompiled[Tuple2.of(a, b)] })) } + return ret.toMap() + } + + override fun compile(pattern: ReflexiveTransitiveClosure): Map> { + val opCompiled = pattern.op.accept(this) + val uuid = Random().nextInt() + val consts = + events + .map { a -> + events.map { b -> + val const = + if (pattern.patternName != null) { + val const = + namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] + ?: Const("RTC_" + uuid + "_" + a + "_" + b, Bool()) + namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] = const + const + } else { + Const("RTC_" + uuid + "_" + a + "_" + b, Bool()) + } + Pair(Tuple2.of(a, b), const) + } + } + .flatten() + .toMap() - val constraints = And(events.map { a -> + val constraints = + And( + events + .map { a -> events.map { b -> - Iff(Or(opCompiled[Tuple2.of(a, b)], Or(events.filter { c -> a != c && b != c }.map { c -> - Or( + Iff( + Or( + opCompiled[Tuple2.of(a, b)], + Or( + events.map { c -> + Or( And(opCompiled[Tuple2.of(a, c)], checkNotNull(consts[Tuple2.of(c, b)]).ref), - And(checkNotNull(consts[Tuple2.of(a, c)]).ref, opCompiled[Tuple2.of(c, b)]) - ) - })), checkNotNull(consts[Tuple2.of(a, b)]).ref) - } - }.flatten()) - transitiveConstraints.add(constraints) - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), checkNotNull(consts[Tuple2.of(a, b)]).ref) + And(checkNotNull(consts[Tuple2.of(a, c)]).ref, opCompiled[Tuple2.of(c, b)]), + ) + } + ), + ), + checkNotNull(consts[Tuple2.of(a, b)]).ref, + ) } + } + .flatten() + ) + transitiveConstraints.add(constraints) + val ret = + events.map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), if (a == b) True() else checkNotNull(consts[Tuple2.of(a, b)]).ref) } - return ret.flatten().toMap() - } - - override fun compile(pattern: Union): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> - events.map { b -> - Pair(Tuple2.of(a, b), Or(op1Compiled[Tuple2.of(a, b)], op2Compiled[Tuple2.of(a, b)])) - } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: Self): Map> = pattern.op.accept(this) + + override fun compile(pattern: Sequence): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + events.map { b -> + Pair( + Tuple2.of(a, b), + Or( + events + .filter { c -> a != c && b != c } + .map { c -> And(op1Compiled[Tuple2.of(a, c)], op2Compiled[Tuple2.of(c, b)]) } + ), + ) } - return ret.flatten().toMap() - } - - override fun compile(pattern: UnionNode): Map> { - val op1Compiled = pattern.op1.accept(this) - val op2Compiled = pattern.op2.accept(this) - - val ret = events.map { a -> Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(a)])) } - return ret.toMap() - } - - override fun compile(pattern: BasicEventSet): Map> { - return events.associate { a -> - Pair(Tuple1.of(a), - when (facts[Pair(pattern.name, Tuple1.of(a))]) { - ThreeVL.FALSE -> False() - ThreeVL.TRUE -> True() - ThreeVL.UNKNOWN, null -> namedLookup.getOrElse(Pair(pattern.name, Tuple1.of(a))) { - val cnst = Const(pattern.name + "_" + a, Bool()) - namedLookup[Pair(pattern.name, Tuple1.of(a))] = cnst - cnst - }.ref - }) + } + return ret.flatten().toMap() + } + + override fun compile(pattern: Toid): Map> { + val opCompiled = pattern.op.accept(this) + val ret = + events.map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), if (a == b) checkNotNull(opCompiled[Tuple1.of(a)]) else False()) } - } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: TransitiveClosure): Map> { + val uuid = Random().nextInt() + val opCompiled = pattern.op.accept(this) + val consts = + events + .map { a -> + events.map { b -> + val const = + if (pattern.patternName != null) { + val const = + namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] + ?: Const("TC_" + uuid + "_" + a + "_" + b, Bool()) + namedLookup[Pair(pattern.patternName!!, Tuple2.of(a, b))] = const + const + } else { + Const("TC_" + uuid + "_" + a + "_" + b, Bool()) + } + Pair(Tuple2.of(a, b), const) + } + } + .flatten() + .toMap() - override fun compile(pattern: BasicRelation): Map> { - return events.map { a -> + val constraints = + And( + events + .map { a -> events.map { b -> - Pair(Tuple2.of(a, b), - when (facts[Pair(pattern.name, Tuple2.of(a, b))]) { - ThreeVL.FALSE -> False() - ThreeVL.TRUE -> True() - ThreeVL.UNKNOWN, null -> namedLookup.getOrElse(Pair(pattern.name, Tuple2.of(a, b))) { - val cnst = Const(pattern.name + "_" + a + "-" + b, Bool()) - namedLookup[Pair(pattern.name, Tuple2.of(a, b))] = cnst - cnst - }.ref - }) + Iff( + Or( + opCompiled[Tuple2.of(a, b)], + Or( + events + .filter { c -> a != c && b != c } + .map { c -> + Or( + And( + opCompiled[Tuple2.of(a, c)], + checkNotNull(consts[Tuple2.of(c, b)]).ref, + ), + And( + checkNotNull(consts[Tuple2.of(a, c)]).ref, + opCompiled[Tuple2.of(c, b)], + ), + ) + } + ), + ), + checkNotNull(consts[Tuple2.of(a, b)]).ref, + ) } - }.flatten().toMap() + } + .flatten() + ) + transitiveConstraints.add(constraints) + val ret = + events.map { a -> + events.map { b -> Pair(Tuple2.of(a, b), checkNotNull(consts[Tuple2.of(a, b)]).ref) } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: Union): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + events.map { b -> + Pair(Tuple2.of(a, b), Or(op1Compiled[Tuple2.of(a, b)], op2Compiled[Tuple2.of(a, b)])) + } + } + return ret.flatten().toMap() + } + + override fun compile(pattern: UnionNode): Map> { + val op1Compiled = pattern.op1.accept(this) + val op2Compiled = pattern.op2.accept(this) + + val ret = + events.map { a -> + Pair(Tuple1.of(a), And(op1Compiled[Tuple1.of(a)], op2Compiled[Tuple1.of(a)])) + } + return ret.toMap() + } + + override fun compile(pattern: BasicEventSet): Map> { + return events.associate { a -> + Pair( + Tuple1.of(a), + when (facts[Pair(pattern.name, Tuple1.of(a))]) { + ThreeVL.FALSE -> False() + ThreeVL.TRUE -> True() + ThreeVL.UNKNOWN, + null -> + namedLookup + .getOrElse(Pair(pattern.name, Tuple1.of(a))) { + val cnst = Const(pattern.name + "_" + a, Bool()) + namedLookup[Pair(pattern.name, Tuple1.of(a))] = cnst + cnst + } + .ref + }, + ) } -} \ No newline at end of file + } + + override fun compile(pattern: BasicRelation): Map> { + return events + .map { a -> + events.map { b -> + Pair( + Tuple2.of(a, b), + when (facts[Pair(pattern.name, Tuple2.of(a, b))]) { + ThreeVL.FALSE -> False() + ThreeVL.TRUE -> True() + ThreeVL.UNKNOWN, + null -> + namedLookup + .getOrElse(Pair(pattern.name, Tuple2.of(a, b))) { + val cnst = Const(pattern.name + "_" + a + "-" + b, Bool()) + namedLookup[Pair(pattern.name, Tuple2.of(a, b))] = cnst + cnst + } + .ref + }, + ) + } + } + .flatten() + .toMap() + } +} diff --git a/subprojects/solver/graph-solver/src/test/java/hu/bme/mit/theta/graphsolver/GraphSolverTest.kt b/subprojects/solver/graph-solver/src/test/java/hu/bme/mit/theta/graphsolver/GraphSolverTest.kt index 38aeee7db7..23e7ab23ba 100644 --- a/subprojects/solver/graph-solver/src/test/java/hu/bme/mit/theta/graphsolver/GraphSolverTest.kt +++ b/subprojects/solver/graph-solver/src/test/java/hu/bme/mit/theta/graphsolver/GraphSolverTest.kt @@ -25,330 +25,316 @@ import hu.bme.mit.theta.graphsolver.patterns.patterns.* import hu.bme.mit.theta.graphsolver.solvers.GraphSolver import hu.bme.mit.theta.graphsolver.solvers.SATGraphSolver import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory +import java.util.* import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import org.junit.runners.Parameterized -import java.util.* class GraphSolverTest { - @ParameterizedTest - @MethodSource("data") - fun test( - constraint: GraphConstraint, - compiler: GraphPatternCompiler, - graphEvents: List, - graphEdges: Map, ThreeVL>, - solver: GraphSolver, - allowed: Boolean, - ) { - compiler.addEvents(graphEvents) - compiler.addFacts(graphEdges) - val compiledConstraint = constraint.accept(compiler) - solver.add(compiledConstraint) - val status = solver.check() - Assertions.assertEquals(allowed, status.isSat) - } + @ParameterizedTest + @MethodSource("data") + fun test( + constraint: GraphConstraint, + compiler: GraphPatternCompiler, + graphEvents: List, + graphEdges: Map, ThreeVL>, + solver: GraphSolver, + allowed: Boolean, + ) { + compiler.addEvents(graphEvents) + compiler.addFacts(graphEdges) + val compiledConstraint = constraint.accept(compiler) + solver.add(compiledConstraint) + val status = solver.check() + Assertions.assertEquals(allowed, status.isSat) + } - companion object { + companion object { - private val smallLine: Pair, Map, ThreeVL>> = Pair(listOf(1, 2, 3), mapOf( - Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.FALSE), - )) + private val smallLine: Pair, Map, ThreeVL>> = + Pair( + listOf(1, 2, 3), + mapOf( + Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.FALSE), + ), + ) - private val smallCycle: Pair, Map, ThreeVL>> = Pair(listOf(1, 2, 3), mapOf( - Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.FALSE), - Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.FALSE), - )) + private val smallCycle: Pair, Map, ThreeVL>> = + Pair( + listOf(1, 2, 3), + mapOf( + Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.FALSE), + Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.FALSE), + ), + ) - private val smallFull: Pair, Map, ThreeVL>> = Pair(listOf(1, 2, 3), mapOf( - Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.TRUE), - Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.TRUE), - Pair(Pair("W", Tuple1.of(1)), ThreeVL.TRUE), - Pair(Pair("R", Tuple1.of(2)), ThreeVL.TRUE), - Pair(Pair("F", Tuple1.of(3)), ThreeVL.TRUE), - )) + private val smallFull: Pair, Map, ThreeVL>> = + Pair( + listOf(1, 2, 3), + mapOf( + Pair(Pair("po", Tuple2.of(1, 1)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(1, 2)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(1, 3)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(2, 1)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(2, 2)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(2, 3)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 1)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 2)), ThreeVL.TRUE), + Pair(Pair("po", Tuple2.of(3, 3)), ThreeVL.TRUE), + Pair(Pair("W", Tuple1.of(1)), ThreeVL.TRUE), + Pair(Pair("R", Tuple1.of(2)), ThreeVL.TRUE), + Pair(Pair("F", Tuple1.of(3)), ThreeVL.TRUE), + ), + ) - @Parameterized.Parameters - @JvmStatic - fun data(): Collection> { - return Arrays.asList( - arrayOf( - Acyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallLine.first, - smallLine.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Acyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallCycle.first, - smallCycle.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Acyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Cyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallLine.first, - smallLine.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Cyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallCycle.first, - smallCycle.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Cyclic(BasicRelation("po")), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Reflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallLine.first, - smallLine.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Reflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallCycle.first, - smallCycle.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Reflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Irreflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallLine.first, - smallLine.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Irreflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallCycle.first, - smallCycle.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Irreflexive(BasicRelation("po")), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Irreflexive(CartesianProduct(BasicEventSet("W"), BasicEventSet("R"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Irreflexive(IdentityClosure(BasicRelation("rf"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - arrayOf( - Empty(Difference(BasicRelation("po"), BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(DifferenceNode(Domain(BasicRelation("po")), Range(BasicRelation("po")))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(Complement(BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(ComplementNode(Domain(BasicRelation("po")))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(EmptyRel), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(EmptySet), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(Intersection(EmptyRel, BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(IntersectionNode(EmptySet, BasicEventSet("W"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(Complement(Union(Self(BasicRelation("po")), BasicRelation("po")))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Nonempty(Union(ReflexiveTransitiveClosure(BasicRelation("po")), BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(UnionNode(BasicEventSet("W"), BasicEventSet("R"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(Difference(Inverse(BasicRelation("po")), BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Empty(Difference(Sequence(BasicRelation("po"), BasicRelation("po")), BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - true - ), - arrayOf( - Nonempty(Difference(Toid(BasicEventSet("W")), BasicRelation("po"))), - Pattern2ExprCompiler(), - smallFull.first, - smallFull.second, - SATGraphSolver( - Z3LegacySolverFactory.getInstance().createSolver()), - false - ), - ) - } + @Parameterized.Parameters + @JvmStatic + fun data(): Collection> { + return Arrays.asList( + arrayOf( + Acyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallLine.first, + smallLine.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Acyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallCycle.first, + smallCycle.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Acyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Cyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallLine.first, + smallLine.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Cyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallCycle.first, + smallCycle.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Cyclic(BasicRelation("po")), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Reflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallLine.first, + smallLine.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Reflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallCycle.first, + smallCycle.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Reflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Irreflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallLine.first, + smallLine.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Irreflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallCycle.first, + smallCycle.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Irreflexive(BasicRelation("po")), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Irreflexive(CartesianProduct(BasicEventSet("W"), BasicEventSet("R"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Irreflexive(IdentityClosure(BasicRelation("rf"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + arrayOf( + Empty(Difference(BasicRelation("po"), BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(DifferenceNode(Domain(BasicRelation("po")), Range(BasicRelation("po")))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(Complement(BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(ComplementNode(Domain(BasicRelation("po")))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(EmptyRel), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(EmptySet), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(Intersection(EmptyRel, BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(IntersectionNode(EmptySet, BasicEventSet("W"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(Complement(Union(Self(BasicRelation("po")), BasicRelation("po")))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Nonempty(Union(ReflexiveTransitiveClosure(BasicRelation("po")), BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(UnionNode(BasicEventSet("W"), BasicEventSet("R"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty(Difference(Inverse(BasicRelation("po")), BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Empty( + Difference(Sequence(BasicRelation("po"), BasicRelation("po")), BasicRelation("po")) + ), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + true, + ), + arrayOf( + Nonempty(Difference(Toid(BasicEventSet("W")), BasicRelation("po"))), + Pattern2ExprCompiler(), + smallFull.first, + smallFull.second, + SATGraphSolver(Z3LegacySolverFactory.getInstance().createSolver()), + false, + ), + ) } -} \ No newline at end of file + } +} diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolver.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolver.java index 7e7609a612..032fced121 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolver.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolver.java @@ -15,7 +15,11 @@ */ package hu.bme.mit.theta.solver.javasmt; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; import hu.bme.mit.theta.core.decl.Decl; @@ -27,7 +31,6 @@ import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; import hu.bme.mit.theta.core.type.bvtype.BvType; -import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.solver.Solver; @@ -35,6 +38,12 @@ import hu.bme.mit.theta.solver.Stack; import hu.bme.mit.theta.solver.UCSolver; import hu.bme.mit.theta.solver.impl.StackImpl; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; import org.sosy_lab.java_smt.api.BasicProverEnvironment; import org.sosy_lab.java_smt.api.BooleanFormula; import org.sosy_lab.java_smt.api.Formula; @@ -43,16 +52,6 @@ import org.sosy_lab.java_smt.api.SolverContext; import org.sosy_lab.java_smt.api.SolverException; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - final class JavaSMTSolver implements UCSolver, Solver { private final JavaSMTSymbolTable symbolTable; @@ -67,15 +66,15 @@ final class JavaSMTSolver implements UCSolver, Solver { private Valuation model; private Collection> unsatCore; - private final Map> assumptions; private SolverStatus status; - public JavaSMTSolver(final JavaSMTSymbolTable symbolTable, - final JavaSMTTransformationManager transformationManager, - final JavaSMTTermTransformer termTransformer, - final SolverContext context, - final BasicProverEnvironment solver) { + public JavaSMTSolver( + final JavaSMTSymbolTable symbolTable, + final JavaSMTTransformationManager transformationManager, + final JavaSMTTermTransformer termTransformer, + final SolverContext context, + final BasicProverEnvironment solver) { this.symbolTable = symbolTable; this.transformationManager = transformationManager; this.termTransformer = termTransformer; @@ -91,8 +90,7 @@ public JavaSMTSolver(final JavaSMTSymbolTable symbolTable, @Override public void add(final Expr assertion) { checkNotNull(assertion); - final BooleanFormula term = (BooleanFormula) transformationManager.toTerm( - assertion); + final BooleanFormula term = (BooleanFormula) transformationManager.toTerm(assertion); add(assertion, term); } @@ -108,7 +106,6 @@ Object add(final Expr assertion, final BooleanFormula term) { return ret; } - @Override public void track(final Expr assertion) { add(assertion); @@ -126,7 +123,6 @@ public Collection> getUnsatCore() { return Collections.unmodifiableCollection(unsatCore); } - private Collection> extractUnsatCore() { assert status == SolverStatus.UNSAT; assert unsatCore == null; @@ -145,7 +141,6 @@ private Collection> extractUnsatCore() { return unsatCore; } - @Override public SolverStatus check() { try { @@ -235,6 +230,11 @@ public void close() { context.close(); } + @Override + public ImmutableMap getStatistics() { + return solver.getStatistics(); + } + //// private final class JavaSMTModel extends Valuation { @@ -276,7 +276,8 @@ public Optional> eval(final Decl tVal = (LitExpr) val; + @SuppressWarnings("unchecked") + final LitExpr tVal = (LitExpr) val; return Optional.ofNullable(tVal); } @@ -342,7 +343,8 @@ public Map, LitExpr> toMap() { private Collection> constDeclsOf(final Model model) { final ImmutableList.Builder> builder = ImmutableList.builder(); - for (final Formula symbol : model.asList().stream().map(ValueAssignment::getKey).toList()) { + for (final Formula symbol : + model.asList().stream().map(ValueAssignment::getKey).toList()) { if (symbolTable.definesSymbol(symbol)) { final ConstDecl constDecl = symbolTable.getConst(symbol); builder.add(constDecl); @@ -351,5 +353,4 @@ private Collection> constDeclsOf(final Model model) { return builder.build(); } } - } diff --git a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt index 9383d479b8..8f416abf73 100644 --- a/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt +++ b/subprojects/solver/solver-smtlib/src/test/java/hu/bme/mit/theta/solver/smtlib/GenericSmtLibHornSolverTest.kt @@ -35,174 +35,181 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource - class GenericSmtLibHornSolverTest { - companion object { - - private var solverManager: SmtLibSolverManager? = null - private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() - - private val SOLVERS: List> = listOf( - Pair("z3", "4.13.0"), - Pair("z3", "4.12.6"), - Pair("eldarica", "2.1"), - Pair("golem", "0.5.0"), - ) - - @JvmStatic - fun solvers(): List { - return SOLVERS.map { Arguments.of(it) } - } - - @BeforeAll - @JvmStatic - fun init() { - if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { - val home = SmtLibSolverManager.HOME - - solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) - for ((solver, version) in SOLVERS) { - - try { - solverManager!!.install(solver, version, version, null, false) - } catch (e: SmtLibSolverInstallerException) { - e.printStackTrace() - } - - solverFactories.put(Pair(solver, version), solverManager!!.getSolverFactory(solver, version)) - } - } - } - - @AfterAll - @JvmStatic - fun destroy() { - for ((solver, version) in SOLVERS) { - try { - solverManager?.uninstall(solver, version) - } catch (e: SmtLibSolverInstallerException) { - e.printStackTrace() - } - } - } + companion object { + + private var solverManager: SmtLibSolverManager? = null + private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() + + private val SOLVERS: List> = + listOf( + Pair("z3", "4.13.0"), + Pair("z3", "4.12.6"), + Pair("eldarica", "2.1"), + Pair("golem", "0.5.0"), + ) + + @JvmStatic + fun solvers(): List { + return SOLVERS.map { Arguments.of(it) } } - @BeforeEach - fun before() { - Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) - } - - @ParameterizedTest(name = "[{index}] {0}") - @MethodSource("solvers") - fun testSolvable(name: Pair) { - val solverFactory = solverFactories[name]!! - val solver = solverFactory.createHornSolver() - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init = Relation("init", Int(), Int()) - val inv = Relation("inv", Int(), Int()) - - init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) - inv(p[0], p[1]) += init(p[0], p[1]).expr - inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) - - !(inv(p[0], p[1]) with Lt(p[1], Int(1))) - - hornSolver.add(init) - hornSolver.add(inv) - - val status = hornSolver.check() - Assertions.assertTrue(status.isSat) - val model = hornSolver.model.toMap() - - Assertions.assertTrue(model.containsKey(inv.constDecl)) - Assertions.assertTrue(model.containsKey(init.constDecl)) - - val checkerSolver = solverFactories.filter { it.key.first.equals("z3") }.values.first().createSolver() - checkerSolver.use { - val p0 = Const("p0", Int()) - val p1 = Const("p1", Int()) - checkerSolver.add( - App( - App( - model.get(init.constDecl) as FuncLitExpr>, - p0.ref), - p1.ref)) - - checkerSolver.add(Lt(p1.ref, Int(0))) - Assertions.assertTrue(checkerSolver.check().isUnsat) - System.err.println(model.toMap()) - } + @BeforeAll + @JvmStatic + fun init() { + if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { + val home = SmtLibSolverManager.HOME + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) + for ((solver, version) in SOLVERS) { + + try { + solverManager!!.install(solver, version, version, null, false) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + + solverFactories.put( + Pair(solver, version), + solverManager!!.getSolverFactory(solver, version), + ) } + } } - @ParameterizedTest(name = "[{index}] {0}") - @MethodSource("solvers") - fun testUnsolvable(name: Pair) { - val solverFactory = solverFactories[name]!! - val solver = solverFactory.createHornSolver() - - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init = Relation("init", Int(), Int()) - val inv = Relation("inv", Int(), Int()) - - init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) - inv(p[0], p[1]) += init(p[0], p[1]).expr - inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) - - !(inv(p[0], p[1]) with Leq(p[1], Int(1))) - - hornSolver.add(init) - hornSolver.add(inv) - - val status = hornSolver.check() - Assertions.assertTrue(status.isUnsat) - - val proof = hornSolver.proof - Assertions.assertTrue(proof != null) - System.err.println(proof) + @AfterAll + @JvmStatic + fun destroy() { + for ((solver, version) in SOLVERS) { + try { + solverManager?.uninstall(solver, version) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() } + } } + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testSolvable(name: Pair) { + val solverFactory = solverFactories[name]!! + val solver = solverFactory.createHornSolver() + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Lt(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isSat) + val model = hornSolver.model.toMap() + + Assertions.assertTrue(model.containsKey(inv.constDecl)) + Assertions.assertTrue(model.containsKey(init.constDecl)) + + val checkerSolver = + solverFactories.filter { it.key.first.equals("z3") }.values.first().createSolver() + checkerSolver.use { + val p0 = Const("p0", Int()) + val p1 = Const("p1", Int()) + checkerSolver.add( + App( + App( + model.get(init.constDecl) as FuncLitExpr>, + p0.ref, + ), + p1.ref, + ) + ) - @ParameterizedTest(name = "[{index}] {0}") - @MethodSource("solvers") - fun testNonlinearUnsolvable(name: Pair) { - val solverFactory = solverFactories[name]!! - val solver = solverFactory.createHornSolver() - - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init1 = Relation("init1", Int(), Int()) - val init2 = Relation("init2", Int(), Int()) - val inv1 = Relation("inv1", Int(), Int()) - val inv2 = Relation("inv2", Int(), Int()) + checkerSolver.add(Lt(p1.ref, Int(0))) + Assertions.assertTrue(checkerSolver.check().isUnsat) + System.err.println(model.toMap()) + } + } + } - val err = Relation("err", Int(), Int()) + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testUnsolvable(name: Pair) { + val solverFactory = solverFactories[name]!! + val solver = solverFactory.createHornSolver() - init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) - init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) - inv1(p[0], p[1]) += init1(p[0], p[1]).expr - inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) - inv2(p[0], p[1]) += init2(p[0], p[1]).expr - inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) - err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) - !err(p[0], p[1]) + !(inv(p[0], p[1]) with Leq(p[1], Int(1))) - hornSolver.add(init1) - hornSolver.add(init2) - hornSolver.add(inv1) - hornSolver.add(inv2) - hornSolver.add(err) + hornSolver.add(init) + hornSolver.add(inv) - val status = hornSolver.check() - Assertions.assertTrue(status.isUnsat) + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) - val proof = hornSolver.proof - Assertions.assertTrue(proof != null) - System.err.println(proof) - } + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + System.err.println(proof) + } + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testNonlinearUnsolvable(name: Pair) { + val solverFactory = solverFactories[name]!! + val solver = solverFactory.createHornSolver() + + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init1 = Relation("init1", Int(), Int()) + val init2 = Relation("init2", Int(), Int()) + val inv1 = Relation("inv1", Int(), Int()) + val inv2 = Relation("inv2", Int(), Int()) + + val err = Relation("err", Int(), Int()) + + init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + inv1(p[0], p[1]) += init1(p[0], p[1]).expr + inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) + inv2(p[0], p[1]) += init2(p[0], p[1]).expr + inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) + + err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) + + !err(p[0], p[1]) + + hornSolver.add(init1) + hornSolver.add(init2) + hornSolver.add(inv1) + hornSolver.add(inv2) + hornSolver.add(err) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) + System.err.println(proof) } + } } diff --git a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java index fb3fb679a5..f3d6b95678 100644 --- a/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java +++ b/subprojects/solver/solver-z3/src/main/java/hu/bme/mit/theta/solver/z3/Z3Solver.java @@ -15,8 +15,13 @@ */ package hu.bme.mit.theta.solver.z3; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.microsoft.z3.FuncDecl; +import com.microsoft.z3.Statistics; import com.microsoft.z3.Status; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.decl.ConstDecl; @@ -32,12 +37,11 @@ import hu.bme.mit.theta.core.type.enumtype.EnumLitExpr; import hu.bme.mit.theta.core.type.enumtype.EnumType; import hu.bme.mit.theta.core.type.functype.FuncType; +import hu.bme.mit.theta.solver.*; import hu.bme.mit.theta.solver.Solver; import hu.bme.mit.theta.solver.SolverStatus; import hu.bme.mit.theta.solver.Stack; -import hu.bme.mit.theta.solver.*; import hu.bme.mit.theta.solver.impl.StackImpl; - import java.util.*; import java.util.ArrayList; import java.util.Collection; @@ -46,9 +50,6 @@ import java.util.Map; import java.util.Optional; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - class Z3Solver implements UCSolver, Solver { protected final Z3SymbolTable symbolTable; @@ -68,10 +69,12 @@ class Z3Solver implements UCSolver, Solver { protected Collection> unsatCore; protected SolverStatus status; - public Z3Solver(final Z3SymbolTable symbolTable, - final Z3TransformationManager transformationManager, - final Z3TermTransformer termTransformer, final com.microsoft.z3.Context z3Context, - final com.microsoft.z3.Solver z3Solver) { + public Z3Solver( + final Z3SymbolTable symbolTable, + final Z3TransformationManager transformationManager, + final Z3TermTransformer termTransformer, + final com.microsoft.z3.Context z3Context, + final com.microsoft.z3.Solver z3Solver) { this.symbolTable = symbolTable; this.transformationManager = transformationManager; this.termTransformer = termTransformer; @@ -87,8 +90,8 @@ public Z3Solver(final Z3SymbolTable symbolTable, @Override public void add(final Expr assertion) { checkNotNull(assertion); - final com.microsoft.z3.BoolExpr term = (com.microsoft.z3.BoolExpr) transformationManager.toTerm( - assertion); + final com.microsoft.z3.BoolExpr term = + (com.microsoft.z3.BoolExpr) transformationManager.toTerm(assertion); add(assertion, term); } @@ -103,8 +106,8 @@ public void track(final Expr assertion) { checkNotNull(assertion); assertions.add(assertion); - final com.microsoft.z3.BoolExpr term = (com.microsoft.z3.BoolExpr) transformationManager.toTerm( - assertion); + final com.microsoft.z3.BoolExpr term = + (com.microsoft.z3.BoolExpr) transformationManager.toTerm(assertion); final String label = String.format(ASSUMPTION_LABEL, labelNum++); final com.microsoft.z3.BoolExpr labelTerm = z3Context.mkBoolConst(label); @@ -235,6 +238,19 @@ public void close() { z3Context.interrupt(); } + @Override + public ImmutableMap getStatistics() { + Statistics stats = z3Solver.getStatistics(); + ImmutableMap.Builder builder = ImmutableMap.builder(); + Set keys = new HashSet<>(); + for (String key : stats.getKeys()) { + if (keys.contains(key)) continue; + builder.put(key, stats.get(key).getValueString()); + keys.add(key); + } + return builder.buildOrThrow(); + } + //// private final class Z3Model extends Valuation { @@ -276,7 +292,8 @@ public Optional> eval(final Decl tVal = (LitExpr) val; + @SuppressWarnings("unchecked") + final LitExpr tVal = (LitExpr) val; return Optional.ofNullable(tVal); } @@ -297,14 +314,14 @@ private LitExpr extractLiteral(final ConstDecl extractFuncLiteral(final FuncDecl funcDecl) { - final Expr expr = termTransformer.toFuncLitExpr(funcDecl, z3Model, - new ArrayList<>()); + final Expr expr = + termTransformer.toFuncLitExpr(funcDecl, z3Model, new ArrayList<>()); return (LitExpr) expr; } private LitExpr extractArrayLiteral(final FuncDecl funcDecl) { - final Expr expr = termTransformer.toArrayLitExpr(funcDecl, z3Model, - new ArrayList<>()); + final Expr expr = + termTransformer.toArrayLitExpr(funcDecl, z3Model, new ArrayList<>()); return (LitExpr) expr; } @@ -317,7 +334,8 @@ private LitExpr extractBvConstLiteral(final FuncDecl funcDecl) { } } - private LitExpr extractEnumLiteral(final ConstDecl constDecl, final FuncDecl funcDecl) { + private LitExpr extractEnumLiteral( + final ConstDecl constDecl, final FuncDecl funcDecl) { final com.microsoft.z3.Expr term = z3Model.getConstInterp(funcDecl); if (term == null) { return null; @@ -359,5 +377,4 @@ private Collection> constDeclsOf(final com.microsoft.z3.Model z3Mod return builder.build(); } } - } diff --git a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt index 859165d9cf..ea3b85aba6 100644 --- a/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt +++ b/subprojects/solver/solver-z3/src/test/java/hu/bme/mit/theta/solver/z3/Z3HornSolverTest.kt @@ -28,131 +28,131 @@ import hu.bme.mit.theta.core.type.functype.FuncType import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.type.inttype.IntType import hu.bme.mit.theta.solver.HornSolver +import java.util.stream.Stream import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream - class Z3HornSolverTest { - companion object { - - @JvmStatic - fun solvers(): Stream { - return Stream.of( - Arguments.of(Z3SolverFactory.getInstance().createHornSolver()), - ) - } - } + companion object { - @BeforeEach - fun before() { - Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + @JvmStatic + fun solvers(): Stream { + return Stream.of(Arguments.of(Z3SolverFactory.getInstance().createHornSolver())) } - - @ParameterizedTest - @MethodSource("solvers") - fun testSolvable(solver: HornSolver) { - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init = Relation("init", Int(), Int()) - val inv = Relation("inv", Int(), Int()) - - init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) - inv(p[0], p[1]) += init(p[0], p[1]).expr - inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) - - !(inv(p[0], p[1]) with Lt(p[1], Int(1))) - - hornSolver.add(init) - hornSolver.add(inv) - - val status = hornSolver.check() - Assertions.assertTrue(status.isSat) - val model = hornSolver.model.toMap() - - Assertions.assertTrue(model.containsKey(inv.constDecl)) - Assertions.assertTrue(model.containsKey(init.constDecl)) - - val checkerSolver = Z3SolverFactory.getInstance().createSolver() - checkerSolver.use { - val p0 = Const("p0", Int()) - val p1 = Const("p1", Int()) - checkerSolver.add( - App( - App( - model.get(init.constDecl) as FuncLitExpr>, - p0.ref), - p1.ref)) - - checkerSolver.add(Lt(p1.ref, Int(0))) - Assertions.assertTrue(checkerSolver.check().isUnsat) - } - } + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + + @ParameterizedTest + @MethodSource("solvers") + fun testSolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) + + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + + !(inv(p[0], p[1]) with Lt(p[1], Int(1))) + + hornSolver.add(init) + hornSolver.add(inv) + + val status = hornSolver.check() + Assertions.assertTrue(status.isSat) + val model = hornSolver.model.toMap() + + Assertions.assertTrue(model.containsKey(inv.constDecl)) + Assertions.assertTrue(model.containsKey(init.constDecl)) + + val checkerSolver = Z3SolverFactory.getInstance().createSolver() + checkerSolver.use { + val p0 = Const("p0", Int()) + val p1 = Const("p1", Int()) + checkerSolver.add( + App( + App( + model.get(init.constDecl) as FuncLitExpr>, + p0.ref, + ), + p1.ref, + ) + ) + + checkerSolver.add(Lt(p1.ref, Int(0))) + Assertions.assertTrue(checkerSolver.check().isUnsat) + } } + } - @ParameterizedTest - @MethodSource("solvers") - fun testUnsolvable(solver: HornSolver) { - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init = Relation("init", Int(), Int()) - val inv = Relation("inv", Int(), Int()) + @ParameterizedTest + @MethodSource("solvers") + fun testUnsolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init = Relation("init", Int(), Int()) + val inv = Relation("inv", Int(), Int()) - init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) - inv(p[0], p[1]) += init(p[0], p[1]).expr - inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) + init(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(1)) + inv(p[0], p[1]) += init(p[0], p[1]).expr + inv(p[0], p[2]) += inv(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(1))) - !(inv(p[0], p[1]) with Leq(p[1], Int(1))) + !(inv(p[0], p[1]) with Leq(p[1], Int(1))) - hornSolver.add(init) - hornSolver.add(inv) + hornSolver.add(init) + hornSolver.add(inv) - val status = hornSolver.check() - Assertions.assertTrue(status.isUnsat) + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) - val proof = hornSolver.proof - Assertions.assertTrue(proof != null) - } + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) } - - @ParameterizedTest - @MethodSource("solvers") - fun testNonlinearUnsolvable(solver: HornSolver) { - solver.use { hornSolver -> - val p = ParamHolder(Int()) - val init1 = Relation("init1", Int(), Int()) - val init2 = Relation("init2", Int(), Int()) - val inv1 = Relation("inv1", Int(), Int()) - val inv2 = Relation("inv2", Int(), Int()) - - val err = Relation("err", Int(), Int()) - - init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) - init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) - inv1(p[0], p[1]) += init1(p[0], p[1]).expr - inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) - inv2(p[0], p[1]) += init2(p[0], p[1]).expr - inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) - - err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) - - !err(p[0], p[1]) - - hornSolver.add(init1) - hornSolver.add(init2) - hornSolver.add(inv1) - hornSolver.add(inv2) - hornSolver.add(err) - - val status = hornSolver.check() - Assertions.assertTrue(status.isUnsat) - - val proof = hornSolver.proof - Assertions.assertTrue(proof != null) - } + } + + @ParameterizedTest + @MethodSource("solvers") + fun testNonlinearUnsolvable(solver: HornSolver) { + solver.use { hornSolver -> + val p = ParamHolder(Int()) + val init1 = Relation("init1", Int(), Int()) + val init2 = Relation("init2", Int(), Int()) + val inv1 = Relation("inv1", Int(), Int()) + val inv2 = Relation("inv2", Int(), Int()) + + val err = Relation("err", Int(), Int()) + + init1(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + init2(p[0], p[1]) += Eq(p[0], Int(0)) + Eq(p[1], Int(0)) + inv1(p[0], p[1]) += init1(p[0], p[1]).expr + inv1(p[0], p[2]) += inv1(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(3))) + inv2(p[0], p[1]) += init2(p[0], p[1]).expr + inv2(p[0], p[2]) += inv2(p[0], p[1]).expr + Eq(p[2], Add(p[1], Int(5))) + + err(p[0], p[2]) += inv1(p[0], p[1]).expr + inv2(p[0], p[1]).expr + Gt(p[1], Int(0)) + + !err(p[0], p[1]) + + hornSolver.add(init1) + hornSolver.add(init2) + hornSolver.add(inv1) + hornSolver.add(inv2) + hornSolver.add(err) + + val status = hornSolver.check() + Assertions.assertTrue(status.isUnsat) + + val proof = hornSolver.proof + Assertions.assertTrue(proof != null) } + } } diff --git a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java index 9fcb6373d9..e10ef15783 100644 --- a/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java +++ b/subprojects/solver/solver/src/main/java/hu/bme/mit/theta/solver/SolverBase.java @@ -15,15 +15,14 @@ */ package hu.bme.mit.theta.solver; +import com.google.common.collect.ImmutableMap; import hu.bme.mit.theta.core.model.Valuation; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.booltype.BoolType; - import java.util.Collection; public interface SolverBase extends AutoCloseable { - /** * Check if the currently added expressions are satisfiable. * @@ -44,16 +43,12 @@ public interface SolverBase extends AutoCloseable { */ void pop(final int n); - /** - * Remove expressions added after the previous {@link #push()} call. - */ + /** Remove expressions added after the previous {@link #push()} call. */ default void pop() { pop(1); } - /** - * Reset the solver state. - */ + /** Reset the solver state. */ void reset(); /** @@ -77,4 +72,13 @@ default void pop() { * @return Expressions */ Collection> getAssertions(); + + /** + * Get the statistics of the solver. + * + * @return Statistics + */ + default ImmutableMap getStatistics() { + return ImmutableMap.of(); + } } diff --git a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt index b4a1356e07..0c922fc0b7 100644 --- a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt +++ b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/FrontendXcfaBuilder.kt @@ -49,6 +49,7 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPo import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory +import hu.bme.mit.theta.xcfa.AssignStmtLabel import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.CPasses import java.math.BigInteger @@ -310,11 +311,9 @@ class FrontendXcfaBuilder( } is RefExpr<*> -> { - StmtLabel( - Stmts.Assign( - cast(lValue.decl as VarDecl<*>, (lValue.decl as VarDecl<*>).type), - cast(CComplexType.getType(lValue, parseContext).castTo(rExpression), lValue.type), - ), + AssignStmtLabel( + lValue, + cast(CComplexType.getType(lValue, parseContext).castTo(rExpression), lValue.type), metadata = getMetadata(statement), ) } diff --git a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/Utils.kt b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/Utils.kt index 40d06bd9c1..8ca2968aae 100644 --- a/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/Utils.kt +++ b/subprojects/xcfa/c2xcfa/src/main/java/hu/bme/mit/theta/c2xcfa/Utils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.c2xcfa import hu.bme.mit.theta.c.frontend.dsl.gen.CLexer @@ -25,45 +24,53 @@ import hu.bme.mit.theta.frontend.getStatistics import hu.bme.mit.theta.frontend.transformation.grammar.function.FunctionVisitor import hu.bme.mit.theta.frontend.transformation.model.statements.CProgram import hu.bme.mit.theta.xcfa.model.XCFA +import java.io.InputStream import org.antlr.v4.runtime.BailErrorStrategy import org.antlr.v4.runtime.CharStreams import org.antlr.v4.runtime.CommonTokenStream -import java.io.InputStream -fun getXcfaFromC(stream: InputStream, parseContext: ParseContext, collectStatistics: Boolean, - checkOverflow: Boolean, warningLogger: Logger): Triple?> { - val input = CharStreams.fromStream(stream) - val lexer = CLexer(input) - val tokens = CommonTokenStream(lexer) - val parser = CParser(tokens) - parser.errorHandler = BailErrorStrategy() - val context = parser.compilationUnit() +fun getXcfaFromC( + stream: InputStream, + parseContext: ParseContext, + collectStatistics: Boolean, + checkOverflow: Boolean, + warningLogger: Logger, +): Triple?> { + val input = CharStreams.fromStream(stream) + val lexer = CLexer(input) + val tokens = CommonTokenStream(lexer) + val parser = CParser(tokens) + parser.errorHandler = BailErrorStrategy() + val context = parser.compilationUnit() - val program = context.accept(FunctionVisitor(parseContext, warningLogger)) - check(program is CProgram) + val program = context.accept(FunctionVisitor(parseContext, warningLogger)) + check(program is CProgram) - val frontendXcfaBuilder = FrontendXcfaBuilder(parseContext, checkOverflow, warningLogger) - val builder = frontendXcfaBuilder.buildXcfa(program) - val xcfa = builder.build() + val frontendXcfaBuilder = FrontendXcfaBuilder(parseContext, checkOverflow, warningLogger) + val builder = frontendXcfaBuilder.buildXcfa(program) + val xcfa = builder.build() - if (collectStatistics) { - val programStatistics = try { - program.getStatistics() - } catch (_: Exception) { - CStatistics(0, emptyList()) - } - val unoptimizedXcfaStatistics = try { - builder.getStatistics() - } catch (_: Exception) { - XcfaStatistics(0, emptyList()) - } - val optimizedXcfaStatistics = try { - xcfa.getStatistics() - } catch (_: Exception) { - XcfaStatistics(0, emptyList()) - } - return Triple(xcfa, programStatistics, Pair(unoptimizedXcfaStatistics, optimizedXcfaStatistics)) - } + if (collectStatistics) { + val programStatistics = + try { + program.getStatistics() + } catch (_: Exception) { + CStatistics(0, emptyList()) + } + val unoptimizedXcfaStatistics = + try { + builder.getStatistics() + } catch (_: Exception) { + XcfaStatistics(0, emptyList()) + } + val optimizedXcfaStatistics = + try { + xcfa.getStatistics() + } catch (_: Exception) { + XcfaStatistics(0, emptyList()) + } + return Triple(xcfa, programStatistics, Pair(unoptimizedXcfaStatistics, optimizedXcfaStatistics)) + } - return Triple(xcfa, null, null) -} \ No newline at end of file + return Triple(xcfa, null, null) +} diff --git a/subprojects/xcfa/cat/src/main/java/hu/bme/mit/theta/cat/dsl/CatVisitor.kt b/subprojects/xcfa/cat/src/main/java/hu/bme/mit/theta/cat/dsl/CatVisitor.kt index 6fc85e623d..a9205be7f3 100644 --- a/subprojects/xcfa/cat/src/main/java/hu/bme/mit/theta/cat/dsl/CatVisitor.kt +++ b/subprojects/xcfa/cat/src/main/java/hu/bme/mit/theta/cat/dsl/CatVisitor.kt @@ -27,244 +27,247 @@ import java.io.IOException class CatVisitor(file: File) : CatBaseVisitor() { - private var mcm: MutableList? = null - private val ruleNameCnt = 0 - private val file: File - private val relations: MutableMap - private val functions: MutableMap - private val procedures: MutableMap - private val paramAssignment: MutableMap - - init { - relations = LinkedHashMap() - procedures = LinkedHashMap() - functions = LinkedHashMap() - paramAssignment = LinkedHashMap() - this.file = file - } - - fun getMcm(): Collection? { - return mcm - } - - private fun getOrCreateRelation(name: String, arity: Int): GraphPattern? { - if (paramAssignment.containsKey(name)) { - return paramAssignment[name] - } - if (relations.containsKey(name)) { - return relations[name] - } - return if (arity == 1) BasicEventSet(name) else BasicRelation(name) - } - - override fun visitMcm(ctx: McmContext): GraphPattern { - mcm = ArrayList() - val file = File(file.parent + File.separator + "stdlib.cat") - visitIncludedFile(file) - return super.visitMcm(ctx) ?: EmptyRel - } - - private fun visitIncludedFile(file: File) { - if (file.exists() && file.isFile) { - try { - val context = CatDslManager.setupCatAntlr(file) - context.scopeBody().accept(this) - } catch (e: IOException) { - e.printStackTrace() - } - } else throw RuntimeException(FileNotFoundException(file.absolutePath)) - } - - override fun visitIncludeFile(ctx: IncludeFileContext): GraphPattern { - val file = File(file.parent + File.separator + ctx.file.text) - visitIncludedFile(file) - return EmptyRel - } - - override fun visitAcyclicDefinition(ctx: AcyclicDefinitionContext): GraphPattern { - val rule = ctx.e.accept(this) as EdgePattern - mcm!!.add(if (ctx.negate == null) Acyclic(rule) else Cyclic(rule)) - return EmptyRel - } - - override fun visitIrreflexiveDefinition(ctx: IrreflexiveDefinitionContext): GraphPattern { - val rule = ctx.e.accept(this) as EdgePattern - mcm!!.add(if (ctx.negate == null) Irreflexive(rule) else Reflexive(rule)) - return EmptyRel - } - - override fun visitEmptyDefinition(ctx: EmptyDefinitionContext): GraphPattern { - val rule = ctx.e.accept(this) - mcm!!.add(if (ctx.negate == null) Empty(rule) else Nonempty(rule)) - return EmptyRel - } - - override fun visitFunctionDef(ctx: FunctionDefContext): GraphPattern { - functions[ctx.n.text] = ctx - return EmptyRel - } - - override fun visitProcDef(ctx: ProcDefContext): GraphPattern { - procedures[ctx.n.text] = ctx - return EmptyRel - } - - override fun visitExprToid(ctx: ExprToidContext): GraphPattern { - val rel = ctx.e.accept(this) as NodePattern - return Toid(rel) - } - - override fun visitExprRange(ctx: ExprRangeContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return Range(rel) - } - - override fun visitExprDomain(ctx: ExprDomainContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return Domain(rel) - } - - override fun visitProcCall(ctx: ProcCallContext): GraphPattern { - val procDefContext = procedures[ctx.NAME().text] - Preconditions.checkNotNull(procDefContext, "Procedure with name " + ctx.NAME().text + " does not exist.") - val e = ctx.params - val toReset: MutableMap = LinkedHashMap() - for (i in e.indices) { - val text = procDefContext!!.params[i].text - val expressionContext = e[i] - toReset[text] = paramAssignment[text] - paramAssignment[text] = expressionContext.accept(this) - } - val accept = procDefContext!!.body.accept(this) - toReset.forEach { (s: String, graphPattern: GraphPattern?) -> - if (graphPattern == null) paramAssignment.remove(s) else paramAssignment[s] = graphPattern - } - return accept - } - - override fun visitExprTryWith(ctx: ExprTryWithContext): GraphPattern { - return ctx.e.accept(this) - } - - override fun visitExprFunctionCall(ctx: ExprFunctionCallContext): GraphPattern { - val functionDefContext = functions[ctx.NAME().text] - Preconditions.checkNotNull(functionDefContext, "Function with name " + ctx.NAME().text + " does not exist.") - val e = ctx.e - val toReset: MutableMap = LinkedHashMap() - for (i in e.indices) { - val text = functionDefContext!!.params[i].text - val expressionContext = e[i] - toReset[text] = paramAssignment[text] - paramAssignment[text] = expressionContext.accept(this) - } - val accept = functionDefContext!!.e.accept(this) - toReset.forEach { (s: String, graphPattern: GraphPattern?) -> - if (graphPattern == null) paramAssignment.remove(s) else paramAssignment[s] = graphPattern - } - return accept - } - - override fun visitLetDefinition(ctx: LetDefinitionContext): GraphPattern { - var name = ctx.NAME().text - var eCtx = ctx.e - relations.remove(name) - for (letAndDefinitionContext in ctx.letAndDefinition()) { - name = letAndDefinitionContext.NAME().text - relations.remove(name) - } - var rel = eCtx.accept(this) - rel.patternName = ctx.NAME().text - relations[ctx.NAME().text] = rel - for (letAndDefinitionContext in ctx.letAndDefinition()) { - name = letAndDefinitionContext.NAME().text - eCtx = letAndDefinitionContext.e - rel = eCtx.accept(this) - relations[name] = rel - } - return rel - } - - override fun visitExprCartesian(ctx: ExprCartesianContext): GraphPattern { - val rel1 = ctx.e1.accept(this) as NodePattern - val rel2 = ctx.e2.accept(this) as NodePattern - return CartesianProduct(rel1, rel2) - } - - override fun visitExprBasic(ctx: ExprBasicContext): GraphPattern { - return getOrCreateRelation(ctx.NAME().text, if (Character.isLowerCase(ctx.NAME().text[0])) 2 else 1)!! - } - - override fun visitExprMinus(ctx: ExprMinusContext): GraphPattern { - val rel1 = ctx.e1.accept(this) - val rel2 = ctx.e2.accept(this) - return if (rel1 is EdgePattern && rel2 is EdgePattern) - Difference(rel1, rel2) - else if (rel1 is NodePattern && rel2 is NodePattern) - DifferenceNode(rel1, rel2) - else error("Mismatched types") - } - - override fun visitExprUnion(ctx: ExprUnionContext): GraphPattern { - val rel1 = ctx.e1.accept(this) - val rel2 = ctx.e2.accept(this) - return if (rel1 is EdgePattern && rel2 is EdgePattern) - Union(rel1, rel2) - else if (rel1 is NodePattern && rel2 is NodePattern) - UnionNode(rel1, rel2) - else error("Mismatched types") - } - - override fun visitExprComposition(ctx: ExprCompositionContext): GraphPattern { - val rel1 = ctx.e1.accept(this) as EdgePattern - val rel2 = ctx.e2.accept(this) as EdgePattern - return Sequence(rel1, rel2) - } - - override fun visitExprIntersection(ctx: ExprIntersectionContext): GraphPattern { - val rel1 = ctx.e1.accept(this) - val rel2 = ctx.e2.accept(this) - return if (rel1 is EdgePattern && rel2 is EdgePattern) - Intersection(rel1, rel2) - else if (rel1 is NodePattern && rel2 is NodePattern) - IntersectionNode(rel1, rel2) - else error("Mismatched types") - } - - override fun visitExprTransitive(ctx: ExprTransitiveContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return TransitiveClosure(rel) - } - - override fun visitExprComplement(ctx: ExprComplementContext): GraphPattern { - val rel = ctx.e.accept(this) - return if (rel is EdgePattern) Complement(rel) else if (rel is NodePattern) ComplementNode(rel) else error( - "Mismatched types") - } - - override fun visitExprInverse(ctx: ExprInverseContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return Inverse(rel) - } - - override fun visitExprTransRef(ctx: ExprTransRefContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return ReflexiveTransitiveClosure(rel) - } - - override fun visitExpr(ctx: CatParser.ExprContext): GraphPattern { - return ctx.e.accept(this) - } - - override fun visitExprOptional(ctx: ExprOptionalContext): GraphPattern { - val rel = ctx.e.accept(this) as EdgePattern - return IdentityClosure(rel) - } - - override fun visitExprNull(ctx: ExprNullContext): GraphPattern { - return EmptyRel - } - - override fun toString(): String { - return mcm.toString() - } -} \ No newline at end of file + private var mcm: MutableList? = null + private val ruleNameCnt = 0 + private val file: File + private val relations: MutableMap + private val functions: MutableMap + private val procedures: MutableMap + private val paramAssignment: MutableMap + + init { + relations = LinkedHashMap() + procedures = LinkedHashMap() + functions = LinkedHashMap() + paramAssignment = LinkedHashMap() + this.file = file + } + + fun getMcm(): Collection? { + return mcm + } + + private fun getOrCreateRelation(name: String, arity: Int): GraphPattern? { + if (paramAssignment.containsKey(name)) { + return paramAssignment[name] + } + if (relations.containsKey(name)) { + return relations[name] + } + return if (arity == 1) BasicEventSet(name) else BasicRelation(name) + } + + override fun visitMcm(ctx: McmContext): GraphPattern { + mcm = ArrayList() + val file = File(file.parent + File.separator + "stdlib.cat") + visitIncludedFile(file) + return super.visitMcm(ctx) ?: EmptyRel + } + + private fun visitIncludedFile(file: File) { + if (file.exists() && file.isFile) { + try { + val context = CatDslManager.setupCatAntlr(file) + context.scopeBody().accept(this) + } catch (e: IOException) { + e.printStackTrace() + } + } else throw RuntimeException(FileNotFoundException(file.absolutePath)) + } + + override fun visitIncludeFile(ctx: IncludeFileContext): GraphPattern { + val file = File(file.parent + File.separator + ctx.file.text) + visitIncludedFile(file) + return EmptyRel + } + + override fun visitAcyclicDefinition(ctx: AcyclicDefinitionContext): GraphPattern { + val rule = ctx.e.accept(this) as EdgePattern + mcm!!.add(if (ctx.negate == null) Acyclic(rule) else Cyclic(rule)) + return EmptyRel + } + + override fun visitIrreflexiveDefinition(ctx: IrreflexiveDefinitionContext): GraphPattern { + val rule = ctx.e.accept(this) as EdgePattern + mcm!!.add(if (ctx.negate == null) Irreflexive(rule) else Reflexive(rule)) + return EmptyRel + } + + override fun visitEmptyDefinition(ctx: EmptyDefinitionContext): GraphPattern { + val rule = ctx.e.accept(this) + mcm!!.add(if (ctx.negate == null) Empty(rule) else Nonempty(rule)) + return EmptyRel + } + + override fun visitFunctionDef(ctx: FunctionDefContext): GraphPattern { + functions[ctx.n.text] = ctx + return EmptyRel + } + + override fun visitProcDef(ctx: ProcDefContext): GraphPattern { + procedures[ctx.n.text] = ctx + return EmptyRel + } + + override fun visitExprToid(ctx: ExprToidContext): GraphPattern { + val rel = ctx.e.accept(this) as NodePattern + return Toid(rel) + } + + override fun visitExprRange(ctx: ExprRangeContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return Range(rel) + } + + override fun visitExprDomain(ctx: ExprDomainContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return Domain(rel) + } + + override fun visitProcCall(ctx: ProcCallContext): GraphPattern { + val procDefContext = procedures[ctx.NAME().text] + Preconditions.checkNotNull( + procDefContext, + "Procedure with name " + ctx.NAME().text + " does not exist.", + ) + val e = ctx.params + val toReset: MutableMap = LinkedHashMap() + for (i in e.indices) { + val text = procDefContext!!.params[i].text + val expressionContext = e[i] + toReset[text] = paramAssignment[text] + paramAssignment[text] = expressionContext.accept(this) + } + val accept = procDefContext!!.body.accept(this) + toReset.forEach { (s: String, graphPattern: GraphPattern?) -> + if (graphPattern == null) paramAssignment.remove(s) else paramAssignment[s] = graphPattern + } + return accept + } + + override fun visitExprTryWith(ctx: ExprTryWithContext): GraphPattern { + return ctx.e.accept(this) + } + + override fun visitExprFunctionCall(ctx: ExprFunctionCallContext): GraphPattern { + val functionDefContext = functions[ctx.NAME().text] + Preconditions.checkNotNull( + functionDefContext, + "Function with name " + ctx.NAME().text + " does not exist.", + ) + val e = ctx.e + val toReset: MutableMap = LinkedHashMap() + for (i in e.indices) { + val text = functionDefContext!!.params[i].text + val expressionContext = e[i] + toReset[text] = paramAssignment[text] + paramAssignment[text] = expressionContext.accept(this) + } + val accept = functionDefContext!!.e.accept(this) + toReset.forEach { (s: String, graphPattern: GraphPattern?) -> + if (graphPattern == null) paramAssignment.remove(s) else paramAssignment[s] = graphPattern + } + return accept + } + + override fun visitLetDefinition(ctx: LetDefinitionContext): GraphPattern { + var name = ctx.NAME().text + var eCtx = ctx.e + relations.remove(name) + for (letAndDefinitionContext in ctx.letAndDefinition()) { + name = letAndDefinitionContext.NAME().text + relations.remove(name) + } + var rel = eCtx.accept(this) + rel.patternName = ctx.NAME().text + relations[ctx.NAME().text] = rel + for (letAndDefinitionContext in ctx.letAndDefinition()) { + name = letAndDefinitionContext.NAME().text + eCtx = letAndDefinitionContext.e + rel = eCtx.accept(this) + relations[name] = rel + } + return rel + } + + override fun visitExprCartesian(ctx: ExprCartesianContext): GraphPattern { + val rel1 = ctx.e1.accept(this) as NodePattern + val rel2 = ctx.e2.accept(this) as NodePattern + return CartesianProduct(rel1, rel2) + } + + override fun visitExprBasic(ctx: ExprBasicContext): GraphPattern { + return getOrCreateRelation( + ctx.NAME().text, + if (Character.isLowerCase(ctx.NAME().text[0])) 2 else 1, + )!! + } + + override fun visitExprMinus(ctx: ExprMinusContext): GraphPattern { + val rel1 = ctx.e1.accept(this) + val rel2 = ctx.e2.accept(this) + return if (rel1 is EdgePattern && rel2 is EdgePattern) Difference(rel1, rel2) + else if (rel1 is NodePattern && rel2 is NodePattern) DifferenceNode(rel1, rel2) + else error("Mismatched types") + } + + override fun visitExprUnion(ctx: ExprUnionContext): GraphPattern { + val rel1 = ctx.e1.accept(this) + val rel2 = ctx.e2.accept(this) + return if (rel1 is EdgePattern && rel2 is EdgePattern) Union(rel1, rel2) + else if (rel1 is NodePattern && rel2 is NodePattern) UnionNode(rel1, rel2) + else error("Mismatched types") + } + + override fun visitExprComposition(ctx: ExprCompositionContext): GraphPattern { + val rel1 = ctx.e1.accept(this) as EdgePattern + val rel2 = ctx.e2.accept(this) as EdgePattern + return Sequence(rel1, rel2) + } + + override fun visitExprIntersection(ctx: ExprIntersectionContext): GraphPattern { + val rel1 = ctx.e1.accept(this) + val rel2 = ctx.e2.accept(this) + return if (rel1 is EdgePattern && rel2 is EdgePattern) Intersection(rel1, rel2) + else if (rel1 is NodePattern && rel2 is NodePattern) IntersectionNode(rel1, rel2) + else error("Mismatched types") + } + + override fun visitExprTransitive(ctx: ExprTransitiveContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return TransitiveClosure(rel) + } + + override fun visitExprComplement(ctx: ExprComplementContext): GraphPattern { + val rel = ctx.e.accept(this) + return if (rel is EdgePattern) Complement(rel) + else if (rel is NodePattern) ComplementNode(rel) else error("Mismatched types") + } + + override fun visitExprInverse(ctx: ExprInverseContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return Inverse(rel) + } + + override fun visitExprTransRef(ctx: ExprTransRefContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return ReflexiveTransitiveClosure(rel) + } + + override fun visitExpr(ctx: CatParser.ExprContext): GraphPattern { + return ctx.e.accept(this) + } + + override fun visitExprOptional(ctx: ExprOptionalContext): GraphPattern { + val rel = ctx.e.accept(this) as EdgePattern + return IdentityClosure(rel) + } + + override fun visitExprNull(ctx: ExprNullContext): GraphPattern { + return EmptyRel + } + + override fun toString(): String { + return mcm.toString() + } +} diff --git a/subprojects/xcfa/llvm2xcfa/build.gradle.kts b/subprojects/xcfa/llvm2xcfa/build.gradle.kts index 1888b50a11..8c7d738775 100644 --- a/subprojects/xcfa/llvm2xcfa/build.gradle.kts +++ b/subprojects/xcfa/llvm2xcfa/build.gradle.kts @@ -36,7 +36,9 @@ tasks.test { } val linkTask = task.first() dependsOn(linkTask) - systemProperty("java.library.path", - linkTask.linkedFile.get().asFile.parent + ":/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib") + systemProperty( + "java.library.path", + linkTask.linkedFile.get().asFile.parent + ":/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib", + ) } } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/Utils.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/Utils.kt index cf5d298dab..0c4993fca6 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/Utils.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/Utils.kt @@ -32,52 +32,56 @@ import kotlin.reflect.KProperty fun Map.reverseMapping() = this.entries.associate { it.value to it.key } fun Valuation.changeVars(varLut: Map, VarDecl<*>>): Valuation { - val builder = ImmutableValuation.builder() - for (decl in this.decls) { - builder.put(decl.changeVars(varLut), this.eval(decl).get()) - } - return builder.build() + val builder = ImmutableValuation.builder() + for (decl in this.decls) { + builder.put(decl.changeVars(varLut), this.eval(decl).get()) + } + return builder.build() } internal fun XcfaState.withGeneralizedVars(): S { - val varLookup = processes.mapNotNull { (_, process) -> process.varLookup.peek()?.reverseMapping() } - .reduceOrNull(Map, VarDecl<*>>::plus) ?: mapOf() - return if (sGlobal.isBottom) sGlobal - else sGlobal.getState(varLookup) + val varLookup = + processes + .mapNotNull { (_, process) -> process.varLookup.peek()?.reverseMapping() } + .reduceOrNull(Map, VarDecl<*>>::plus) ?: mapOf() + return if (sGlobal.isBottom) sGlobal else sGlobal.getState(varLookup) } private fun S.getState(varLookup: Map, VarDecl<*>>): S = - when (this) { - is ExplState -> ExplState.of(getVal().changeVars(varLookup)) - is PredState -> PredState.of(preds.map { p -> p.changeVars(varLookup) }) - is PtrState<*> -> PtrState(innerState.getState(varLookup)) - else -> throw NotImplementedError( - "Generalizing variable instances is not implemented for data states that are not explicit or predicate.") - } as S + when (this) { + is ExplState -> ExplState.of(getVal().changeVars(varLookup)) + is PredState -> PredState.of(preds.map { p -> p.changeVars(varLookup) }) + is PtrState<*> -> PtrState(innerState.getState(varLookup)) + else -> + throw NotImplementedError( + "Generalizing variable instances is not implemented for data states that are not explicit or predicate." + ) + } + as S class LazyDelegate(val getProperty: T.() -> P) { - private var calculated = false - private lateinit var property: P + private var calculated = false + private lateinit var property: P - operator fun getValue(thisRef: T, property: KProperty<*>): P { - return if (calculated) this.property - else thisRef.getProperty().also { - this.property = it - this.calculated = true - } - } + operator fun getValue(thisRef: T, property: KProperty<*>): P { + return if (calculated) this.property + else + thisRef.getProperty().also { + this.property = it + this.calculated = true + } + } } val XCFA.isInlined: Boolean by LazyDelegate { - !this.procedures.any { p -> - p.edges.any { e -> - e.getFlatLabels().any { l -> - l is InvokeLabel && this.procedures.any { it.name == l.name } - } - } + !this.procedures.any { p -> + p.edges.any { e -> + e.getFlatLabels().any { l -> l is InvokeLabel && this.procedures.any { it.name == l.name } } } + } } fun XcfaProcessState.foldVarLookup(): Map, VarDecl<*>> = - this.varLookup.reduceRightOrNull { lookup, acc -> acc + lookup } ?: emptyMap() // right map overrides left's keys + this.varLookup.reduceRightOrNull { lookup, acc -> acc + lookup } + ?: emptyMap() // right map overrides left's keys diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaPrecRefiner.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaPrecRefiner.kt index 64e8984d4d..4d85422207 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaPrecRefiner.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaPrecRefiner.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis import com.google.common.base.Preconditions @@ -32,63 +31,76 @@ import hu.bme.mit.theta.core.type.anytype.Dereference import hu.bme.mit.theta.xcfa.model.getTempLookup import hu.bme.mit.theta.xcfa.passes.changeVars -class XcfaPrecRefiner(refToPrec: RefutationToPrec, R>) : - PrecRefiner, XcfaAction, XcfaPrec>, R> { +class XcfaPrecRefiner( + refToPrec: RefutationToPrec, R> +) : PrecRefiner, XcfaAction, XcfaPrec>, R> { - private val refToPrec: RefutationToPrec, R> = Preconditions.checkNotNull(refToPrec) + private val refToPrec: RefutationToPrec, R> = Preconditions.checkNotNull(refToPrec) - override fun refine(prec: XcfaPrec>, trace: Trace, XcfaAction>, - refutation: R): XcfaPrec> { - Preconditions.checkNotNull(trace) - Preconditions.checkNotNull(prec) - Preconditions.checkNotNull(refutation) - var runningPrec: PtrPrec

= prec.p - for (i in trace.states.indices) { - val reverseVarLookup = trace.states[i].processes.values.map { - it.foldVarLookup().map { Pair(it.value, it.key) } - }.flatten().toMap() - val reverseTempLookup = if (i > 0) getTempLookup( - trace.actions[i - 1].edge.label).entries.associateBy( - { it.value }) { it.key } else emptyMap() - val precFromRef = refToPrec.toPrec(refutation, i).changeVars(reverseVarLookup + reverseTempLookup) - runningPrec = refToPrec.join(runningPrec, precFromRef) - } -// if (runningPrec is PredPrec) { -// // todo: instead of outright disabling the dereferences in the prec, maybe just force a literal in the address? -// runningPrec = PredPrec.of(runningPrec.preds.filter { !it.hasDeref() }) as P -// } - return prec.refine(runningPrec) + override fun refine( + prec: XcfaPrec>, + trace: Trace, XcfaAction>, + refutation: R, + ): XcfaPrec> { + Preconditions.checkNotNull(trace) + Preconditions.checkNotNull(prec) + Preconditions.checkNotNull(refutation) + var runningPrec: PtrPrec

= prec.p + for (i in trace.states.indices) { + val reverseVarLookup = + trace.states[i] + .processes + .values + .map { it.foldVarLookup().map { Pair(it.value, it.key) } } + .flatten() + .toMap() + val reverseTempLookup = + if (i > 0) + getTempLookup(trace.actions[i - 1].edge.label).entries.associateBy({ it.value }) { + it.key + } + else emptyMap() + val precFromRef = + refToPrec.toPrec(refutation, i).changeVars(reverseVarLookup + reverseTempLookup) + runningPrec = refToPrec.join(runningPrec, precFromRef) } + // if (runningPrec is PredPrec) { + // // todo: instead of outright disabling the dereferences in the prec, maybe just + // force a literal in the address? + // runningPrec = PredPrec.of(runningPrec.preds.filter { !it.hasDeref() }) as P + // } + return prec.refine(runningPrec) + } - override fun toString(): String { - return javaClass.simpleName - } + override fun toString(): String { + return javaClass.simpleName + } } private fun Expr<*>.hasDeref(): Boolean = - this is Dereference<*, *, *> || this.ops.any(Expr<*>::hasDeref) + this is Dereference<*, *, *> || this.ops.any(Expr<*>::hasDeref) fun

P.changeVars(lookup: Map, VarDecl<*>>): P = - if (lookup.isEmpty()) this - else - when (this) { - is ExplPrec -> ExplPrec.of(vars.map { it.changeVars(lookup) }) as P - is PredPrec -> PredPrec.of(preds.map { it.changeVars(lookup) }) as P - is PtrPrec<*> -> PtrPrec(innerPrec.changeVars(lookup)) as P - else -> error("Precision type ${this.javaClass} not supported.") - } + if (lookup.isEmpty()) this + else + when (this) { + is ExplPrec -> ExplPrec.of(vars.map { it.changeVars(lookup) }) as P + is PredPrec -> PredPrec.of(preds.map { it.changeVars(lookup) }) as P + is PtrPrec<*> -> PtrPrec(innerPrec.changeVars(lookup)) as P + else -> error("Precision type ${this.javaClass} not supported.") + } fun

P.addVars(lookups: Collection, VarDecl<*>>>): P = - if (lookups.isEmpty()) this - else - when (this) { - is ExplPrec -> ExplPrec.of( - vars.map { lookups.map { lookup -> it.changeVars(lookup) } }.flatten()) as P + if (lookups.isEmpty()) this + else + when (this) { + is ExplPrec -> + ExplPrec.of(vars.map { lookups.map { lookup -> it.changeVars(lookup) } }.flatten()) as P - is PredPrec -> PredPrec.of( - preds.map { lookups.map { lookup -> it.changeVars(lookup) } }.flatten()) as P + is PredPrec -> + PredPrec.of(preds.map { lookups.map { lookup -> it.changeVars(lookup) } }.flatten()) as P - is PtrPrec<*> -> PtrPrec(innerPrec.addVars(lookups)) as P + is PtrPrec<*> -> PtrPrec(innerPrec.addVars(lookups)) as P - else -> error("Precision type ${this.javaClass} not supported.") - } + else -> error("Precision type ${this.javaClass} not supported.") + } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaState.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaState.kt index 0054be37f2..c3f66a25fe 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaState.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaState.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis import hu.bme.mit.theta.analysis.expr.ExprState @@ -24,6 +23,7 @@ import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.core.utils.TypeUtils.cast +import hu.bme.mit.theta.xcfa.AssignStmtLabel import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.changeVars @@ -32,298 +32,405 @@ import java.util.* private var pidCnt = 1 private var procCnt = 1 -data class XcfaState @JvmOverloads constructor( - val xcfa: XCFA?, - val processes: Map, - val sGlobal: S, - val mutexes: Map = processes.keys.associateBy { "$it" }, - val threadLookup: Map, Int> = emptyMap(), - val bottom: Boolean = false, +data class XcfaState +@JvmOverloads +constructor( + val xcfa: XCFA?, + val processes: Map, + val sGlobal: S, + val mutexes: Map = processes.keys.associateBy { "$it" }, + val threadLookup: Map, Int> = emptyMap(), + val bottom: Boolean = false, ) : ExprState { - - override fun isBottom(): Boolean { - return bottom || sGlobal.isBottom - } - - override fun toExpr(): Expr { - return sGlobal.toExpr() + override fun isBottom(): Boolean { + return bottom || sGlobal.isBottom + } + + override fun toExpr(): Expr { + return sGlobal.toExpr() + } + + fun apply(a: XcfaAction): Pair, XcfaAction> { + val changes: MutableList<(XcfaState) -> XcfaState> = ArrayList() + if (mutexes[""] != null && mutexes[""] != a.pid) + return Pair(copy(bottom = true), a.withLabel(SequenceLabel(listOf(NopLabel)))) + + val processState = processes[a.pid] + checkNotNull(processState) + check(processState.locs.peek() == a.source) + val newProcesses: MutableMap = LinkedHashMap(processes) + newProcesses[a.pid] = checkNotNull(processes[a.pid]?.withNewLoc(a.target)) + if (processes != newProcesses) { + changes.add { state -> state.withProcesses(newProcesses) } } - fun apply(a: XcfaAction): Pair, XcfaAction> { - val changes: MutableList<(XcfaState) -> XcfaState> = ArrayList() - if (mutexes[""] != null && mutexes[""] != a.pid) return Pair(copy(bottom = true), - a.withLabel(SequenceLabel(listOf(NopLabel)))) - - val processState = processes[a.pid] - checkNotNull(processState) - check(processState.locs.peek() == a.source) - val newProcesses: MutableMap = LinkedHashMap(processes) - newProcesses[a.pid] = checkNotNull(processes[a.pid]?.withNewLoc(a.target)) - if (processes != newProcesses) { - changes.add { state -> state.withProcesses(newProcesses) } - } + val newLabels = + a.edge.getFlatLabels().filter { + when (it) { + is FenceLabel -> + it.labels + .forEach { label -> + when (label) { + "ATOMIC_BEGIN" -> changes.add { it.enterMutex("", a.pid) } + "ATOMIC_END" -> changes.add { it.exitMutex("", a.pid) } + in Regex("mutex_lock\\((.*)\\)") -> + changes.add { state -> + state.enterMutex( + label.substring("mutex_lock".length + 1, label.length - 1), + a.pid, + ) + } - val newLabels = a.edge.getFlatLabels().filter { - when (it) { - is FenceLabel -> it.labels.forEach { label -> - when (label) { - "ATOMIC_BEGIN" -> changes.add { it.enterMutex("", a.pid) } - "ATOMIC_END" -> changes.add { it.exitMutex("", a.pid) } - in Regex("mutex_lock\\((.*)\\)") -> changes.add { state -> - state.enterMutex(label.substring("mutex_lock".length + 1, label.length - 1), a.pid) - } - - in Regex("mutex_unlock\\((.*)\\)") -> changes.add { state -> - state.exitMutex(label.substring("mutex_unlock".length + 1, label.length - 1), a.pid) - } - - in Regex("start_cond_wait\\((.*)\\)") -> { - val args = label.substring("start_cond_wait".length + 1, label.length - 1).split(",") - changes.add { state -> state.enterMutex(args[0], -1) } - changes.add { state -> state.exitMutex(args[1], a.pid) } - } - - in Regex("cond_wait\\((.*)\\)") -> { - val args = label.substring("cond_wait".length + 1, label.length - 1).split(",") - changes.add { state -> state.enterMutex(args[0], a.pid) } - changes.add { state -> state.exitMutex(args[0], a.pid) } - changes.add { state -> state.enterMutex(args[1], a.pid) } - } - - in Regex("cond_signal\\((.*)\\)") -> changes.add { state -> - state.exitMutex(label.substring("cond_signal".length + 1, label.length - 1), -1) - } - - else -> error("Unknown fence label $label") + in Regex("mutex_unlock\\((.*)\\)") -> + changes.add { state -> + state.exitMutex( + label.substring("mutex_unlock".length + 1, label.length - 1), + a.pid, + ) } - }.let { false } - - is InvokeLabel -> { - val proc = xcfa?.procedures?.find { proc -> proc.name == it.name } ?: error( - "No such method ${it.name}.") - val returnStmt = SequenceLabel( - proc.params.withIndex().filter { it.value.second != ParamDirection.IN }.map { iVal -> - StmtLabel(Assign(cast((it.params[iVal.index] as RefExpr<*>).decl as VarDecl<*>, - iVal.value.first.type), cast(iVal.value.first.ref, iVal.value.first.type)), - metadata = it.metadata) - }) + + in Regex("start_cond_wait\\((.*)\\)") -> { + val args = + label.substring("start_cond_wait".length + 1, label.length - 1).split(",") + changes.add { state -> state.enterMutex(args[0], -1) } + changes.add { state -> state.exitMutex(args[1], a.pid) } + } + + in Regex("cond_wait\\((.*)\\)") -> { + val args = label.substring("cond_wait".length + 1, label.length - 1).split(",") + changes.add { state -> state.enterMutex(args[0], a.pid) } + changes.add { state -> state.exitMutex(args[0], a.pid) } + changes.add { state -> state.enterMutex(args[1], a.pid) } + } + + in Regex("cond_signal\\((.*)\\)") -> changes.add { state -> - state.invokeFunction(a.pid, proc, returnStmt, proc.params.toMap(), it.tempLookup) + state.exitMutex( + label.substring("cond_signal".length + 1, label.length - 1), + -1, + ) } - false - } - is ReturnLabel -> changes.add { state -> state.returnFromFunction(a.pid) }.let { true } + "pthread_exit" -> { + if (processState.locs.size > 1) + error("pthread_exit not allowed in invoked function") + } - is JoinLabel -> { - changes.add { state -> state.enterMutex("${threadLookup[it.pidVar]}", a.pid) } - changes.add { state -> state.exitMutex("${threadLookup[it.pidVar]}", a.pid) } - false + else -> error("Unknown fence label $label") } - - is NondetLabel -> true - NopLabel -> false - is ReadLabel -> error("Read/Write labels not yet supported") - is SequenceLabel -> true - is StartLabel -> changes.add { state -> state.start(it) }.let { true } - is StmtLabel -> true - is WriteLabel -> error("Read/Write labels not yet supported") + } + .let { false } + + is InvokeLabel -> { + val proc = + xcfa?.procedures?.find { proc -> proc.name == it.name } + ?: error("No such method ${it.name}.") + val returnStmt = + SequenceLabel( + proc.params + .withIndex() + .filter { it.value.second != ParamDirection.IN } + .map { iVal -> + AssignStmtLabel( + it.params[iVal.index] as RefExpr<*>, + cast(iVal.value.first.ref, iVal.value.first.type), + metadata = it.metadata, + ) + } + ) + changes.add { state -> + state.invokeFunction(a.pid, proc, returnStmt, proc.params.toMap(), it.tempLookup) } + false + } + + is ReturnLabel -> changes.add { state -> state.returnFromFunction(a.pid) }.let { true } + + is JoinLabel -> { + changes.add { state -> state.enterMutex("${threadLookup[it.pidVar]}", a.pid) } + changes.add { state -> state.exitMutex("${threadLookup[it.pidVar]}", a.pid) } + false + } + + is NondetLabel -> true + NopLabel -> false + is ReadLabel -> error("Read/Write labels not yet supported") + is SequenceLabel -> true + is StartLabel -> changes.add { state -> state.start(it) }.let { true } + is StmtLabel -> true + is WriteLabel -> error("Read/Write labels not yet supported") } + } - changes.add { state -> - if (checkNotNull(state.processes[a.pid]).locs.isEmpty()) state.endProcess(a.pid) else state - } - - return Pair(changes.fold(this) { current, change -> change(current) }, a.withLabel(SequenceLabel(newLabels))) + changes.add { state -> + if (checkNotNull(state.processes[a.pid]).locs.isEmpty()) state.endProcess(a.pid) else state } - private fun start(startLabel: StartLabel): XcfaState { - val newProcesses: MutableMap = LinkedHashMap(processes) - val newThreadLookup: MutableMap, Int> = LinkedHashMap(threadLookup) - - val procedure = checkNotNull(xcfa?.procedures?.find { it.name == startLabel.name }) - val paramList = procedure.params.toMap() - val tempLookup = startLabel.tempLookup - val returnStmt = SequenceLabel( - emptyList() // TODO: return values are handled in JoinLabel instead -- how to solve this? -// procedure.params.withIndex().filter { it.value.second != ParamDirection.IN } -// .map { iVal -> -// StmtLabel(Assign( -// cast((startLabel.params[iVal.index] as RefExpr<*>).decl as VarDecl<*>, -// iVal.value.first.type), -// cast(iVal.value.first.ref, iVal.value.first.type)), -// metadata = startLabel.metadata) -// } - ) - - val pid = pidCnt++ - val lookup = XcfaProcessState.createLookup(procedure, "T$pid", "") - newThreadLookup[startLabel.pidVar] = pid - newProcesses[pid] = XcfaProcessState(LinkedList(listOf(procedure.initLoc)), prefix = "T$pid", - varLookup = LinkedList(listOf(lookup)), returnStmts = LinkedList(listOf(returnStmt)), - paramStmts = LinkedList(listOf(Pair( + return Pair( + changes.fold(this) { current, change -> change(current) }, + a.withLabel(SequenceLabel(newLabels)), + ) + } + + private fun start(startLabel: StartLabel): XcfaState { + val newProcesses: MutableMap = LinkedHashMap(processes) + val newThreadLookup: MutableMap, Int> = LinkedHashMap(threadLookup) + + val procedure = checkNotNull(xcfa?.procedures?.find { it.name == startLabel.name }) + val paramList = procedure.params.toMap() + val tempLookup = startLabel.tempLookup + val returnStmt = + SequenceLabel( + emptyList() // TODO: return values are handled in JoinLabel instead -- how to solve this? + // procedure.params.withIndex().filter { it.value.second != ParamDirection.IN } + // .map { iVal -> + // StmtLabel(Assign( + // cast((startLabel.params[iVal.index] as RefExpr<*>).decl as + // VarDecl<*>, + // iVal.value.first.type), + // cast(iVal.value.first.ref, iVal.value.first.type)), + // metadata = startLabel.metadata) + // } + ) + + val pid = pidCnt++ + val lookup = XcfaProcessState.createLookup(procedure, "T$pid", "") + newThreadLookup[startLabel.pidVar] = pid + newProcesses[pid] = + XcfaProcessState( + LinkedList(listOf(procedure.initLoc)), + prefix = "T$pid", + varLookup = LinkedList(listOf(lookup)), + returnStmts = LinkedList(listOf(returnStmt)), + paramStmts = + LinkedList( + listOf( + Pair( /* init */ - SequenceLabel(paramList.filter { it.value != ParamDirection.OUT }.map { - StmtLabel(Assign(cast(it.key.changeVars(lookup), it.key.type), - cast(it.key.changeVars(tempLookup).ref, it.key.type))) - }), + SequenceLabel( + paramList + .filter { it.value != ParamDirection.OUT } + .map { + StmtLabel( + Assign( + cast(it.key.changeVars(lookup), it.key.type), + cast(it.key.changeVars(tempLookup).ref, it.key.type), + ) + ) + } + ), /* deinit */ - SequenceLabel(paramList.filter { it.value != ParamDirection.IN }.map { - StmtLabel(Assign(cast(it.key.changeVars(tempLookup), it.key.type), - cast(it.key.changeVars(lookup).ref, it.key.type))) - }), - )))) - val newMutexes = LinkedHashMap(mutexes) - newMutexes["$pid"] = pid - - return copy(processes = newProcesses, threadLookup = newThreadLookup, mutexes = newMutexes) - } - - private fun endProcess(pid: Int): XcfaState { - val newProcesses: MutableMap = LinkedHashMap(processes) - newProcesses.remove(pid) - val newMutexes = LinkedHashMap(mutexes) - newMutexes.remove("$pid") - return copy(processes = newProcesses, mutexes = newMutexes) - } - - private fun invokeFunction(pid: Int, proc: XcfaProcedure, returnStmt: XcfaLabel, - paramList: Map, ParamDirection>, tempLookup: Map, VarDecl<*>>): XcfaState { - val newProcesses: MutableMap = LinkedHashMap(processes) - newProcesses[pid] = checkNotNull(processes[pid]?.enterFunction(proc, returnStmt, paramList, tempLookup)) - return copy(processes = newProcesses) - } - - private fun returnFromFunction(pid: Int): XcfaState { - val newProcesses: MutableMap = LinkedHashMap(processes) - newProcesses[pid] = checkNotNull(processes[pid]?.exitFunction()) - return copy(processes = newProcesses) - } - - fun enterMutex(key: String, pid: Int): XcfaState { - if (mutexes.keys.any { it == key && mutexes[it] != pid }) return copy(bottom = true) - - val newMutexes = LinkedHashMap(mutexes) - newMutexes[key] = pid - return copy(mutexes = newMutexes) - } - - fun exitMutex(key: String, pid: Int): XcfaState { - val newMutexes = LinkedHashMap(mutexes) - newMutexes.remove(key, pid) - return copy(mutexes = newMutexes) - } - - - private fun withProcesses(nP: Map): XcfaState { - return copy(processes = nP) - } - - fun withState(s: S): XcfaState { - return copy(sGlobal = s) - } - - override fun toString(): String { - return "$processes {$sGlobal, mutex=$mutexes${if (bottom) ", bottom" else ""}}" - } + SequenceLabel( + paramList + .filter { it.value != ParamDirection.IN } + .map { + StmtLabel( + Assign( + cast(it.key.changeVars(tempLookup), it.key.type), + cast(it.key.changeVars(lookup).ref, it.key.type), + ) + ) + } + ), + ) + ) + ), + ) + val newMutexes = LinkedHashMap(mutexes) + newMutexes["$pid"] = pid + + return copy(processes = newProcesses, threadLookup = newThreadLookup, mutexes = newMutexes) + } + + private fun endProcess(pid: Int): XcfaState { + val newProcesses: MutableMap = LinkedHashMap(processes) + newProcesses.remove(pid) + val newMutexes = LinkedHashMap(mutexes) + newMutexes.remove("$pid") + return copy(processes = newProcesses, mutexes = newMutexes) + } + + private fun invokeFunction( + pid: Int, + proc: XcfaProcedure, + returnStmt: XcfaLabel, + paramList: Map, ParamDirection>, + tempLookup: Map, VarDecl<*>>, + ): XcfaState { + val newProcesses: MutableMap = LinkedHashMap(processes) + newProcesses[pid] = + checkNotNull(processes[pid]?.enterFunction(proc, returnStmt, paramList, tempLookup)) + return copy(processes = newProcesses) + } + + private fun returnFromFunction(pid: Int): XcfaState { + val newProcesses: MutableMap = LinkedHashMap(processes) + newProcesses[pid] = checkNotNull(processes[pid]?.exitFunction()) + return copy(processes = newProcesses) + } + + fun enterMutex(key: String, pid: Int): XcfaState { + if (mutexes.keys.any { it == key && mutexes[it] != pid }) return copy(bottom = true) + + val newMutexes = LinkedHashMap(mutexes) + newMutexes[key] = pid + return copy(mutexes = newMutexes) + } + + fun exitMutex(key: String, pid: Int): XcfaState { + val newMutexes = LinkedHashMap(mutexes) + newMutexes.remove(key, pid) + return copy(mutexes = newMutexes) + } + + private fun withProcesses(nP: Map): XcfaState { + return copy(processes = nP) + } + + fun withState(s: S): XcfaState { + return copy(sGlobal = s) + } + + override fun toString(): String { + return "$processes {$sGlobal, mutex=$mutexes${if (bottom) ", bottom" else ""}}" + } } -data class XcfaProcessState(val locs: LinkedList, val varLookup: LinkedList, VarDecl<*>>>, - val returnStmts: LinkedList = LinkedList(listOf(NopLabel)), - val paramStmts: LinkedList> = LinkedList(listOf(Pair(NopLabel, NopLabel))), - val paramsInitialized: Boolean = false, val prefix: String = "") { - - internal var popped: XcfaLocation? = null // stores if the stack was popped due to abstract stack covering - - fun withNewLoc(l: XcfaLocation): XcfaProcessState { - val deque: LinkedList = LinkedList(locs) - deque.pop() - deque.push(l) - return copy(locs = deque, paramsInitialized = true) - } - - override fun toString(): String = when (locs.size) { - 0 -> "" - 1 -> locs.peek()!!.toString() + " initialized=$paramsInitialized" - else -> "${locs.peek()!!} [${locs.size}], initilized=$paramsInitialized" - } - - fun enterFunction(xcfaProcedure: XcfaProcedure, returnStmt: XcfaLabel, paramList: Map, ParamDirection>, - tempLookup: Map, VarDecl<*>>): XcfaProcessState { - val deque: LinkedList = LinkedList(locs) - val varLookup: LinkedList, VarDecl<*>>> = LinkedList(varLookup) - val returnStmts: LinkedList = LinkedList(returnStmts) - val paramStmts: LinkedList> = LinkedList(paramStmts) - deque.push(xcfaProcedure.initLoc) - val lookup = createLookup(xcfaProcedure, prefix, "P${procCnt++}") - varLookup.push(lookup) - returnStmts.push(returnStmt) - paramStmts.push(Pair( - /* init */ - SequenceLabel(paramList.filter { it.value != ParamDirection.OUT }.map { - StmtLabel(Assign(cast(it.key.changeVars(lookup), it.key.type), - cast(it.key.changeVars(tempLookup).ref, it.key.type))) - }), - /* deinit */ - SequenceLabel(paramList.filter { it.value != ParamDirection.IN }.map { - StmtLabel(Assign(cast(it.key.changeVars(tempLookup), it.key.type), - cast(it.key.changeVars(lookup).ref, it.key.type))) - }), - )) - return copy(locs = deque, varLookup = varLookup, returnStmts = returnStmts, paramStmts = paramStmts, - paramsInitialized = false) - } - - fun exitFunction(): XcfaProcessState { - val deque: LinkedList = LinkedList(locs) - val varLookup: LinkedList, VarDecl<*>>> = LinkedList(varLookup) - val returnStmts: LinkedList = LinkedList(returnStmts) - val paramStmts: LinkedList> = LinkedList(paramStmts) - deque.pop() - varLookup.pop() - returnStmts.pop() - paramStmts.pop() - return copy(locs = deque, varLookup = varLookup, returnStmts = returnStmts, paramStmts = paramStmts) +data class XcfaProcessState( + val locs: LinkedList, + val varLookup: LinkedList, VarDecl<*>>>, + val returnStmts: LinkedList = LinkedList(listOf(NopLabel)), + val paramStmts: LinkedList> = + LinkedList(listOf(Pair(NopLabel, NopLabel))), + val paramsInitialized: Boolean = false, + val prefix: String = "", +) { + + internal var popped: XcfaLocation? = + null // stores if the stack was popped due to abstract stack covering + + fun withNewLoc(l: XcfaLocation): XcfaProcessState { + val deque: LinkedList = LinkedList(locs) + deque.pop() + deque.push(l) + return copy(locs = deque, paramsInitialized = true) + } + + override fun toString(): String = + when (locs.size) { + 0 -> "" + 1 -> locs.peek()!!.toString() + " initialized=$paramsInitialized" + else -> "${locs.peek()!!} [${locs.size}], initilized=$paramsInitialized" } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as XcfaProcessState - - if (locs != other.locs) return false - if (paramsInitialized != other.paramsInitialized) return false - - return true - } - - override fun hashCode(): Int { - var result = locs.hashCode() - result = 31 * result + paramsInitialized.hashCode() - return result - } - - companion object { - - fun createLookup(proc: XcfaProcedure, threadPrefix: String, - procPrefix: String): Map, VarDecl<*>> = listOf(proc.params.map { it.first }, proc.vars).flatten() - .associateWith { - val sj = StringJoiner("::") - if (threadPrefix != "") sj.add(threadPrefix) - else sj.add("_") - if (procPrefix != "") sj.add(procPrefix) - else sj.add("_") - sj.add(it.name) - val name = sj.toString() - if (name != it.name) Var(sj.toString(), it.type) - else it - }.filter { it.key != it.value } - } - - + fun enterFunction( + xcfaProcedure: XcfaProcedure, + returnStmt: XcfaLabel, + paramList: Map, ParamDirection>, + tempLookup: Map, VarDecl<*>>, + ): XcfaProcessState { + val deque: LinkedList = LinkedList(locs) + val varLookup: LinkedList, VarDecl<*>>> = LinkedList(varLookup) + val returnStmts: LinkedList = LinkedList(returnStmts) + val paramStmts: LinkedList> = LinkedList(paramStmts) + deque.push(xcfaProcedure.initLoc) + val lookup = createLookup(xcfaProcedure, prefix, "P${procCnt++}") + varLookup.push(lookup) + returnStmts.push(returnStmt) + paramStmts.push( + Pair( + /* init */ + SequenceLabel( + paramList + .filter { it.value != ParamDirection.OUT } + .map { + StmtLabel( + Assign( + cast(it.key.changeVars(lookup), it.key.type), + cast(it.key.changeVars(tempLookup).ref, it.key.type), + ) + ) + } + ), + /* deinit */ + SequenceLabel( + paramList + .filter { it.value != ParamDirection.IN } + .map { + StmtLabel( + Assign( + cast(it.key.changeVars(tempLookup), it.key.type), + cast(it.key.changeVars(lookup).ref, it.key.type), + ) + ) + } + ), + ) + ) + return copy( + locs = deque, + varLookup = varLookup, + returnStmts = returnStmts, + paramStmts = paramStmts, + paramsInitialized = false, + ) + } + + fun exitFunction(): XcfaProcessState { + val deque: LinkedList = LinkedList(locs) + val varLookup: LinkedList, VarDecl<*>>> = LinkedList(varLookup) + val returnStmts: LinkedList = LinkedList(returnStmts) + val paramStmts: LinkedList> = LinkedList(paramStmts) + deque.pop() + varLookup.pop() + returnStmts.pop() + paramStmts.pop() + return copy( + locs = deque, + varLookup = varLookup, + returnStmts = returnStmts, + paramStmts = paramStmts, + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as XcfaProcessState + + if (locs != other.locs) return false + if (paramsInitialized != other.paramsInitialized) return false + + return true + } + + override fun hashCode(): Int { + var result = locs.hashCode() + result = 31 * result + paramsInitialized.hashCode() + return result + } + + companion object { + + fun createLookup( + proc: XcfaProcedure, + threadPrefix: String, + procPrefix: String, + ): Map, VarDecl<*>> = + listOf(proc.params.map { it.first }, proc.vars) + .flatten() + .associateWith { + val sj = StringJoiner("::") + if (threadPrefix != "") sj.add(threadPrefix) else sj.add("_") + if (procPrefix != "") sj.add(procPrefix) else sj.add("_") + sj.add(it.name) + val name = sj.toString() + if (name != it.name) Var(sj.toString(), it.type) else it + } + .filter { it.key != it.value } + } } operator fun Regex.contains(text: CharSequence): Boolean = this.matches(text) diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateAdapter.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateAdapter.kt index 30d7147c72..bcfff87c7d 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateAdapter.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis import com.google.gson.Gson @@ -25,51 +24,54 @@ import hu.bme.mit.theta.core.decl.VarDecl import java.lang.reflect.Type class XcfaStateAdapter(val gsonSupplier: () -> Gson, val stateTypeSupplier: () -> Type) : - TypeAdapter>() { + TypeAdapter>() { + + private lateinit var gson: Gson + private lateinit var stateType: Type - private lateinit var gson: Gson - private lateinit var stateType: Type - override fun write(writer: JsonWriter, value: XcfaState<*>) { - initGson() - writer.beginObject() - writer.name("processes") - gson.toJson(gson.toJsonTree(value.processes), writer) - writer.name("sGlobal") - gson.toJson(gson.toJsonTree(value.sGlobal), writer) - writer.name("mutexes") - gson.toJson(gson.toJsonTree(value.mutexes), writer) - writer.name("threadLookup") - gson.toJson(gson.toJsonTree(value.threadLookup), writer) - writer.name("bottom") - gson.toJson(gson.toJsonTree(value.bottom), writer) - writer.endObject() - } + override fun write(writer: JsonWriter, value: XcfaState<*>) { + initGson() + writer.beginObject() + writer.name("processes") + gson.toJson(gson.toJsonTree(value.processes), writer) + writer.name("sGlobal") + gson.toJson(gson.toJsonTree(value.sGlobal), writer) + writer.name("mutexes") + gson.toJson(gson.toJsonTree(value.mutexes), writer) + writer.name("threadLookup") + gson.toJson(gson.toJsonTree(value.threadLookup), writer) + writer.name("bottom") + gson.toJson(gson.toJsonTree(value.bottom), writer) + writer.endObject() + } - override fun read(reader: JsonReader): XcfaState<*> { - initGson() - if (!this::stateType.isInitialized) stateType = stateTypeSupplier() - reader.beginObject() - check(reader.nextName() == "processes") - val processes: Map = gson.fromJson(reader, Map::class.java) - check(reader.nextName() == "sGlobal") - val sGlobal: ExprState = gson.fromJson(reader, stateType) - check(reader.nextName() == "mutexes") - val mutexes: Map = gson.fromJson(reader, Map::class.java) - check(reader.nextName() == "threadLookup") - val threadLookup: Map, Int> = gson.fromJson(reader, Map::class.java) - check(reader.nextName() == "bottom") - val bottom: Boolean = gson.fromJson(reader, Boolean::class.java) + override fun read(reader: JsonReader): XcfaState<*> { + initGson() + if (!this::stateType.isInitialized) stateType = stateTypeSupplier() + reader.beginObject() + check(reader.nextName() == "processes") + val processes: Map = gson.fromJson(reader, Map::class.java) + check(reader.nextName() == "sGlobal") + val sGlobal: ExprState = gson.fromJson(reader, stateType) + check(reader.nextName() == "mutexes") + val mutexes: Map = gson.fromJson(reader, Map::class.java) + check(reader.nextName() == "threadLookup") + val threadLookup: Map, Int> = gson.fromJson(reader, Map::class.java) + check(reader.nextName() == "bottom") + val bottom: Boolean = gson.fromJson(reader, Boolean::class.java) - reader.endObject() - return XcfaState(xcfa = null, - processes = processes, - sGlobal = sGlobal, - mutexes = mutexes, - threadLookup = threadLookup, - bottom = bottom) - } + reader.endObject() + return XcfaState( + xcfa = null, + processes = processes, + sGlobal = sGlobal, + mutexes = mutexes, + threadLookup = threadLookup, + bottom = bottom, + ) + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } -} \ No newline at end of file + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaToMonolithicExpr.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaToMonolithicExpr.kt index d91174ed82..d73f5644f8 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaToMonolithicExpr.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaToMonolithicExpr.kt @@ -13,100 +13,130 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package hu.bme.mit.theta.xcfa.analysis; +package hu.bme.mit.theta.xcfa.analysis -import com.google.common.base.Preconditions; +import com.google.common.base.Preconditions import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr import hu.bme.mit.theta.analysis.expl.ExplState import hu.bme.mit.theta.analysis.ptr.PtrState -import hu.bme.mit.theta.core.decl.Decls; +import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.model.ImmutableValuation import hu.bme.mit.theta.core.model.Valuation -import hu.bme.mit.theta.core.stmt.AssignStmt; -import hu.bme.mit.theta.core.stmt.AssumeStmt; -import hu.bme.mit.theta.core.stmt.NonDetStmt; -import hu.bme.mit.theta.core.stmt.SequenceStmt; +import hu.bme.mit.theta.core.stmt.AssignStmt +import hu.bme.mit.theta.core.stmt.AssumeStmt +import hu.bme.mit.theta.core.stmt.NonDetStmt +import hu.bme.mit.theta.core.stmt.SequenceStmt import hu.bme.mit.theta.core.type.booltype.BoolExprs.And import hu.bme.mit.theta.core.type.inttype.IntExprs import hu.bme.mit.theta.core.type.inttype.IntExprs.Eq import hu.bme.mit.theta.core.type.inttype.IntExprs.Neq import hu.bme.mit.theta.core.type.inttype.IntLitExpr -import hu.bme.mit.theta.core.utils.StmtUtils; -import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; +import hu.bme.mit.theta.core.utils.StmtUtils +import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory import hu.bme.mit.theta.xcfa.getFlatLabels -import hu.bme.mit.theta.xcfa.model.StmtLabel; -import hu.bme.mit.theta.xcfa.model.XCFA; +import hu.bme.mit.theta.xcfa.model.StmtLabel +import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.model.XcfaEdge -import hu.bme.mit.theta.xcfa.model.XcfaLocation; +import hu.bme.mit.theta.xcfa.model.XcfaLocation import java.util.* fun XCFA.toMonolithicExpr(): MonolithicExpr { - Preconditions.checkArgument(this.initProcedures.size == 1) - val proc = this.initProcedures.stream().findFirst().orElse(null).first - Preconditions.checkArgument(proc.edges.map { it.getFlatLabels() }.flatten().none { it !is StmtLabel }) - Preconditions.checkArgument(proc.errorLoc.isPresent) + Preconditions.checkArgument(this.initProcedures.size == 1) + val proc = this.initProcedures.stream().findFirst().orElse(null).first + Preconditions.checkArgument( + proc.edges.map { it.getFlatLabels() }.flatten().none { it !is StmtLabel } + ) + Preconditions.checkArgument(proc.errorLoc.isPresent) - val map = mutableMapOf() - for ((i, x) in proc.locs.withIndex()) { - map[x] = i; - } - val locVar = Decls.Var("__loc_", IntExprs.Int()) - val tranList = proc.edges.map { (source, target, label): XcfaEdge -> - SequenceStmt.of(listOf( + val map = mutableMapOf() + for ((i, x) in proc.locs.withIndex()) { + map[x] = i + } + val locVar = Decls.Var("__loc_", IntExprs.Int()) + val tranList = + proc.edges + .map { (source, target, label): XcfaEdge -> + SequenceStmt.of( + listOf( AssumeStmt.of(Eq(locVar.ref, IntExprs.Int(map[source]!!))), label.toStmt(), - AssignStmt.of(locVar, - IntExprs.Int(map[target]!!)) - )) - }.toList() - val trans = NonDetStmt.of(tranList) - val transUnfold = StmtUtils.toExpr(trans, VarIndexingFactory.indexing(0)) + AssignStmt.of(locVar, IntExprs.Int(map[target]!!)), + ) + ) + } + .toList() + val trans = NonDetStmt.of(tranList) + val transUnfold = StmtUtils.toExpr(trans, VarIndexingFactory.indexing(0)) - return MonolithicExpr( - initExpr = Eq(locVar.ref, IntExprs.Int(map[proc.initLoc]!!)), - transExpr = And(transUnfold.exprs), - propExpr = Neq(locVar.ref, IntExprs.Int(map[proc.errorLoc.get()]!!)), - transOffsetIndex = transUnfold.indexing - ) + return MonolithicExpr( + initExpr = Eq(locVar.ref, IntExprs.Int(map[proc.initLoc]!!)), + transExpr = And(transUnfold.exprs), + propExpr = Neq(locVar.ref, IntExprs.Int(map[proc.errorLoc.get()]!!)), + transOffsetIndex = transUnfold.indexing, + ) } fun XCFA.valToAction(val1: Valuation, val2: Valuation): XcfaAction { - val val1Map = val1.toMap() - val val2Map = val2.toMap() - var i = 0 - val map: MutableMap = HashMap() - for (x in this.procedures.first { it.name == "main" }.locs) { - map[x] = i++ - } - return XcfaAction( - pid = 0, - edge = this.procedures.first { it.name == "main" }.edges.first { edge -> - map[edge.source] == (val1Map[val1Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() && - map[edge.target] == (val2Map[val2Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() - }) + val val1Map = val1.toMap() + val val2Map = val2.toMap() + var i = 0 + val map: MutableMap = HashMap() + for (x in this.procedures.first { it.name == "main" }.locs) { + map[x] = i++ + } + return XcfaAction( + pid = 0, + edge = + this.procedures + .first { it.name == "main" } + .edges + .first { edge -> + map[edge.source] == + (val1Map[val1Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() && + map[edge.target] == + (val2Map[val2Map.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt() + }, + ) } fun XCFA.valToState(val1: Valuation): XcfaState> { - val valMap = val1.toMap() - var i = 0 - val map: MutableMap = HashMap() - for (x in this.procedures.first { it.name == "main" }.locs) { - map[i++] = x - } - return XcfaState( - xcfa = this, - processes = mapOf(Pair(0, XcfaProcessState( - locs = LinkedList( - listOf(map[(valMap[valMap.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt()])), + val valMap = val1.toMap() + var i = 0 + val map: MutableMap = HashMap() + for (x in this.procedures.first { it.name == "main" }.locs) { + map[i++] = x + } + return XcfaState( + xcfa = this, + processes = + mapOf( + Pair( + 0, + XcfaProcessState( + locs = + LinkedList( + listOf( + map[ + (valMap[valMap.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt()] + ) + ), varLookup = LinkedList(), - ))), - PtrState(ExplState.of( - ImmutableValuation.from( - val1.toMap() - .filter { it.key.name != "__loc_" && !it.key.name.startsWith("__temp_") } - .map { Pair(Decls.Var("_" + "_" + it.key.name, it.key.type), it.value) }.toMap()))), - mutexes = emptyMap(), - threadLookup = emptyMap(), - bottom = false - ) + ), + ) + ), + PtrState( + ExplState.of( + ImmutableValuation.from( + val1 + .toMap() + .filter { it.key.name != "__loc_" && !it.key.name.startsWith("__temp_") } + .map { Pair(Decls.Var("_" + "_" + it.key.name, it.key.type), it.value) } + .toMap() + ) + ) + ), + mutexes = emptyMap(), + threadLookup = emptyMap(), + bottom = false, + ) } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiMultiThread.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiMultiThread.kt index 0500b5d88b..fa3ba5800a 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiMultiThread.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiMultiThread.kt @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis.coi import hu.bme.mit.theta.analysis.LTS import hu.bme.mit.theta.analysis.Prec -import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.collectVarsWithAccessType +import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.isWritten import hu.bme.mit.theta.xcfa.model.StartLabel import hu.bme.mit.theta.xcfa.model.XCFA @@ -29,131 +28,154 @@ import hu.bme.mit.theta.xcfa.pointsTo class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { - private val startThreads: MutableSet = mutableSetOf() - private val edgeToProcedure: MutableMap = mutableMapOf() - private var XcfaEdge.procedure: XcfaProcedure - get() = edgeToProcedure[this]!! - set(value) { - edgeToProcedure[this] = value - } - private val interProcessObservers: MutableMap> = mutableMapOf() - - data class ProcedureEntry( - val procedure: XcfaProcedure, - val scc: Int, - val pid: Int - ) - - override val lts = object : LTS { - override fun getEnabledActionsFor(state: S): Collection { - val enabled = coreLts.getEnabledActionsFor(state) - return lastPrec?.let { replaceIrrelevantActions(state, enabled, it) } ?: enabled - } - - override fun

getEnabledActionsFor(state: S, explored: Collection, prec: P): Collection { - if (lastPrec != prec) reinitialize(prec) - val enabled = coreLts.getEnabledActionsFor(state, explored, prec) - return replaceIrrelevantActions(state, enabled, prec) - } + private val startThreads: MutableSet = mutableSetOf() + private val edgeToProcedure: MutableMap = mutableMapOf() + private var XcfaEdge.procedure: XcfaProcedure + get() = edgeToProcedure[this]!! + set(value) { + edgeToProcedure[this] = value + } - private fun replaceIrrelevantActions(state: S, enabled: Collection, prec: Prec): Collection { - val procedures = state.processes.map { (pid, pState) -> - val loc = pState.locs.peek() - val procedure = loc.incomingEdges.ifEmpty(loc::outgoingEdges).first().procedure - ProcedureEntry(procedure, loc.scc, pid) - }.toMutableList() - - do { - var anyNew = false - startThreads.filter { edge -> - procedures.any { edge.procedure == it.procedure && it.scc >= edge.source.scc } - }.forEach { edge -> - edge.getFlatLabels().filterIsInstance().forEach { startLabel -> - val procedure = xcfa.procedures.find { it.name == startLabel.name }!! - val procedureEntry = ProcedureEntry(procedure, procedure.initLoc.scc, -1) - if (procedureEntry !in procedures) { - procedures.add(procedureEntry) - anyNew = true - } - } - } - } while (anyNew) - val multipleProcedures = findDuplicates(procedures.map { it.procedure }) - - return enabled.map { action -> - if (!isObserved(action, procedures, multipleProcedures)) { - replace(action, prec) - } else { - action.transFuncVersion = null - action - } + private val interProcessObservers: MutableMap> = mutableMapOf() + + data class ProcedureEntry(val procedure: XcfaProcedure, val scc: Int, val pid: Int) + + override val lts = + object : LTS { + override fun getEnabledActionsFor(state: S): Collection { + val enabled = coreLts.getEnabledActionsFor(state) + return lastPrec?.let { replaceIrrelevantActions(state, enabled, it) } ?: enabled + } + + override fun

getEnabledActionsFor( + state: S, + explored: Collection, + prec: P, + ): Collection { + if (lastPrec != prec) reinitialize(prec) + val enabled = coreLts.getEnabledActionsFor(state, explored, prec) + return replaceIrrelevantActions(state, enabled, prec) + } + + private fun replaceIrrelevantActions( + state: S, + enabled: Collection, + prec: Prec, + ): Collection { + val procedures = + state.processes + .map { (pid, pState) -> + val loc = pState.locs.peek() + val procedure = loc.incomingEdges.ifEmpty(loc::outgoingEdges).first().procedure + ProcedureEntry(procedure, loc.scc, pid) } - } + .toMutableList() - private fun isObserved(action: A, procedures: MutableList, - multipleProcedures: Set): Boolean { - val toVisit = edgeToProcedure.keys.filter { - it.source == action.edge.source && it.target == action.edge.target - }.toMutableList() - val visited = mutableSetOf() - - while (toVisit.isNotEmpty()) { - val visiting = toVisit.removeFirst() - if (isRealObserver(visiting)) return true - - visited.add(visiting) - val toAdd = (directObservers[visiting] ?: emptySet()) union - (interProcessObservers[visiting]?.filter { edge -> - procedures.any { - it.procedure.name == edge.procedure.name && it.scc >= edge.source.scc && - (it.procedure.name != visiting.procedure.name || it.procedure in multipleProcedures) - } // the edge is still reachable - } ?: emptySet()) - toVisit.addAll(toAdd.filter { it !in visited }) + do { + var anyNew = false + startThreads + .filter { edge -> + procedures.any { edge.procedure == it.procedure && it.scc >= edge.source.scc } } - return false - } - - fun findDuplicates(list: List): Set { - val seen = mutableSetOf() - val duplicates = mutableSetOf() - for (item in list) { - if (!seen.add(item.name)) { - duplicates.add(item) + .forEach { edge -> + edge.getFlatLabels().filterIsInstance().forEach { startLabel -> + val procedure = xcfa.procedures.find { it.name == startLabel.name }!! + val procedureEntry = ProcedureEntry(procedure, procedure.initLoc.scc, -1) + if (procedureEntry !in procedures) { + procedures.add(procedureEntry) + anyNew = true } + } } - return duplicates + } while (anyNew) + val multipleProcedures = findDuplicates(procedures.map { it.procedure }) + + return enabled.map { action -> + if (!isObserved(action, procedures, multipleProcedures)) { + replace(action, prec) + } else { + action.transFuncVersion = null + action + } } - } - - fun reinitialize(prec: Prec) { - directObservers.clear() - interProcessObservers.clear() - xcfa.procedures.forEach { procedure -> - procedure.edges.forEach { edge -> - edge.procedure = procedure - if (edge.getFlatLabels().any { it is StartLabel }) startThreads.add(edge) - findDirectObservers(edge, prec) - findInterProcessObservers(edge, prec) - } + } + + private fun isObserved( + action: A, + procedures: MutableList, + multipleProcedures: Set, + ): Boolean { + val toVisit = + edgeToProcedure.keys + .filter { it.source == action.edge.source && it.target == action.edge.target } + .toMutableList() + val visited = mutableSetOf() + + while (toVisit.isNotEmpty()) { + val visiting = toVisit.removeFirst() + if (isRealObserver(visiting)) return true + + visited.add(visiting) + val toAdd = + (directObservers[visiting] ?: emptySet()) union + (interProcessObservers[visiting]?.filter { edge -> + procedures.any { + it.procedure.name == edge.procedure.name && + it.scc >= edge.source.scc && + (it.procedure.name != visiting.procedure.name || + it.procedure in multipleProcedures) + } // the edge is still reachable + } ?: emptySet()) + toVisit.addAll(toAdd.filter { it !in visited }) } - lastPrec = prec - } - - private fun findInterProcessObservers(edge: XcfaEdge, prec: Prec) { - val precVars = prec.usedVars - val writtenVars = edge.collectVarsWithAccessType().filter { it.value.isWritten && it.key in precVars } - if (writtenVars.isEmpty()) return - val writtenMemLocs = writtenVars.pointsTo(xcfa) - - xcfa.procedures.forEach { procedure -> - procedure.edges.forEach { - addEdgeIfObserved(edge, it, writtenVars, writtenMemLocs, precVars, interProcessObservers) - } + return false + } + + fun findDuplicates(list: List): Set { + val seen = mutableSetOf() + val duplicates = mutableSetOf() + for (item in list) { + if (!seen.add(item.name)) { + duplicates.add(item) + } } + return duplicates + } } - override fun addToRelation(source: XcfaEdge, target: XcfaEdge, relation: MutableMap>) { - relation[source] = relation.getOrDefault(source, setOf()) + target + fun reinitialize(prec: Prec) { + directObservers.clear() + interProcessObservers.clear() + xcfa.procedures.forEach { procedure -> + procedure.edges.forEach { edge -> + edge.procedure = procedure + if (edge.getFlatLabels().any { it is StartLabel }) startThreads.add(edge) + findDirectObservers(edge, prec) + findInterProcessObservers(edge, prec) + } + } + lastPrec = prec + } + + private fun findInterProcessObservers(edge: XcfaEdge, prec: Prec) { + val precVars = prec.usedVars + val writtenVars = + edge.collectVarsWithAccessType().filter { it.value.isWritten && it.key in precVars } + if (writtenVars.isEmpty()) return + val writtenMemLocs = writtenVars.pointsTo(xcfa) + + xcfa.procedures.forEach { procedure -> + procedure.edges.forEach { + addEdgeIfObserved(edge, it, writtenVars, writtenMemLocs, precVars, interProcessObservers) + } } -} \ No newline at end of file + } + + override fun addToRelation( + source: XcfaEdge, + target: XcfaEdge, + relation: MutableMap>, + ) { + relation[source] = relation.getOrDefault(source, setOf()) + target + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaExactPo.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaExactPo.kt new file mode 100644 index 0000000000..c6b13f72b4 --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaExactPo.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.analysis.oc + +import hu.bme.mit.theta.xcfa.model.XcfaEdge +import hu.bme.mit.theta.xcfa.model.XcfaLocation +import hu.bme.mit.theta.xcfa.model.XcfaProcedure + +internal class XcfaExactPo(private val threads: Set) { + + private val reachableEdges = threads.associate { it.pid to ReachableEdges(it.procedure) } + + private data class Edge(val source: XcfaLocation?, val target: XcfaLocation, val pid: Int) { + + val edge: Pair + get() = source to target + + constructor(event: E) : this(event.edge.source, event.edge.target, event.pid) + } + + fun isPo(from: E?, to: E): Boolean { + from ?: return true + if (from.clkId == to.clkId) return true + val possiblePathPoints = mutableListOf(Edge(from)) + val visited = mutableSetOf() + while (possiblePathPoints.isNotEmpty()) { + val current = possiblePathPoints.removeFirst() + if (!visited.add(current)) continue + if (current.pid == to.pid && reachableEdges[current.pid]!!.reachable(current.edge, to.edge)) + return true + + threads + .filter { + it.startEvent?.pid == current.pid && + reachableEdges[current.pid]!!.reachable(current.edge, it.startEvent.edge) + } + .forEach { thread -> + possiblePathPoints.add(Edge(null, thread.procedure.initLoc, thread.pid)) + } + + threads + .find { it.pid == current.pid } + ?.let { thread -> + thread.joinEvents.forEach { + possiblePathPoints.add(Edge(it.edge.source, it.edge.target, it.pid)) + } + } + } + + return false + } +} + +private class ReachableEdges(procedure: XcfaProcedure) { + + private data class Edge(val source: XcfaLocation?, val target: XcfaLocation) { + constructor(edge: XcfaEdge) : this(edge.source, edge.target) + } + + private infix fun XcfaLocation?.to(other: XcfaLocation) = Edge(this, other) + + private val ids = mutableMapOf() + private var reachable: Array> + + init { + val toVisit = mutableListOf(null to procedure.initLoc) + val initials = mutableListOf>() + while (toVisit.isNotEmpty()) { // assumes xcfa contains no cycles (an OC checker requirement) + val (source, target) = toVisit.removeFirst() + val id = ids.size + ids[source to target] = id + + if (source == procedure.initLoc) { + initials.add(ids[null to procedure.initLoc]!! to id) + } else { + source + ?.incomingEdges + ?.filter { Edge(it) in ids } + ?.forEach { initials.add(ids[Edge(it)]!! to id) } + } + target.outgoingEdges + .filter { Edge(it) in ids } + .forEach { initials.add(id to ids[Edge(it)]!!) } + + val toAdd = + target.outgoingEdges.map { it.source to it.target }.filter { it !in ids && it !in toVisit } + toVisit.addAll(toAdd) + } + reachable = Array(ids.size) { Array(ids.size) { false } } + close(initials) // close reachable transitively + } + + fun reachable(from: Pair, to: XcfaEdge): Boolean = + reachable[ids[from.first to from.second]!!][ids[Edge(to)]!!] + + private fun close(initials: List>) { + val toClose = initials.toMutableList() + while (toClose.isNotEmpty()) { + val (from, to) = toClose.removeFirst() + if (reachable[from][to]) continue + + reachable[from][to] = true + reachable[to].forEachIndexed { i, b -> if (b && !reachable[from][i]) toClose.add(from to i) } + reachable.forEachIndexed { i, b -> if (b[from] && !reachable[i][to]) toClose.add(i to to) } + } + for (i in reachable.indices) reachable[i][i] = true + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt index 72f7a2520e..c9cb77d55d 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt @@ -15,7 +15,8 @@ */ package hu.bme.mit.theta.xcfa.analysis.oc -import hu.bme.mit.theta.analysis.Trace +import hu.bme.mit.theta.analysis.Cex +import hu.bme.mit.theta.analysis.EmptyCex import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.SafetyResult @@ -23,16 +24,20 @@ import hu.bme.mit.theta.analysis.algorithm.oc.EventType import hu.bme.mit.theta.analysis.algorithm.oc.OcChecker import hu.bme.mit.theta.analysis.algorithm.oc.Relation import hu.bme.mit.theta.analysis.algorithm.oc.RelationType -import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.analysis.unit.UnitPrec import hu.bme.mit.theta.common.logging.Logger -import hu.bme.mit.theta.core.decl.* +import hu.bme.mit.theta.core.decl.ConstDecl +import hu.bme.mit.theta.core.decl.Decls +import hu.bme.mit.theta.core.decl.IndexedConstDecl +import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.AssignStmt import hu.bme.mit.theta.core.stmt.AssumeStmt import hu.bme.mit.theta.core.stmt.HavocStmt +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.Type import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq +import hu.bme.mit.theta.core.type.anytype.Dereference import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.booltype.BoolExprs.* import hu.bme.mit.theta.core.type.booltype.BoolType @@ -40,14 +45,15 @@ import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory +import hu.bme.mit.theta.solver.Solver +import hu.bme.mit.theta.solver.SolverStatus import hu.bme.mit.theta.xcfa.* -import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaPrec -import hu.bme.mit.theta.xcfa.analysis.XcfaState import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.AssumeFalseRemovalPass import hu.bme.mit.theta.xcfa.passes.AtomicReadsOneWritePass import hu.bme.mit.theta.xcfa.passes.MutexToVarPass +import kotlin.time.measureTime private val Expr<*>.vars get() = ExprUtils.getVars(this) @@ -56,10 +62,11 @@ class XcfaOcChecker( xcfa: XCFA, decisionProcedure: OcDecisionProcedureType, private val logger: Logger, -) : SafetyChecker>, XcfaAction>, XcfaPrec> { - - private val ocChecker: OcChecker = decisionProcedure.checker() - private val solver = ocChecker.solver + conflictInput: String?, + private val outputConflictClauses: Boolean, + nonPermissiveValidation: Boolean, + private val autoConflictConfig: AutoConflictFinderConfig, +) : SafetyChecker> { private val xcfa: XCFA = xcfa.optimizeFurther( @@ -67,6 +74,7 @@ class XcfaOcChecker( ) private var indexing = VarIndexingFactory.indexing(0) private val localVars = mutableMapOf, MutableMap>>() + private val memoryDecl = Decls.Var("__oc_checker_memory_declaration__", Int()) private val threads = mutableSetOf() private val events = mutableMapOf, MutableMap>>() @@ -74,50 +82,95 @@ class XcfaOcChecker( private val branchingConditions = mutableListOf>() private val pos = mutableListOf() private val rfs = mutableMapOf, MutableSet>() + private val wss = mutableMapOf, MutableSet>() + + private val ocChecker: OcChecker = + if (conflictInput == null) decisionProcedure.checker() + else + XcfaOcCorrectnessValidator( + decisionProcedure, + conflictInput, + threads, + !nonPermissiveValidation, + logger, + ) - override fun check( - prec: XcfaPrec? - ): SafetyResult>, XcfaAction>> = + override fun check(prec: XcfaPrec?): SafetyResult = let { - if (xcfa.initProcedures.size > 1) - error("Multiple entry points are not supported by OC checker.") + if (xcfa.initProcedures.size > 1) exit("multiple entry points") - logger.write(Logger.Level.MAINSTEP, "Adding constraints...\n") - xcfa.initProcedures.forEach { processThread(Thread(it.first)) } + logger.writeln(Logger.Level.MAINSTEP, "Adding constraints...") + xcfa.initProcedures.forEach { ThreadProcessor(Thread(procedure = it.first)).process() } addCrossThreadRelations() - if (!addToSolver()) - return@let SafetyResult.safe>, XcfaAction>>( - EmptyProof.getInstance() - ) // no violations in the model - - logger.write(Logger.Level.MAINSTEP, "Start checking...\n") - val status = ocChecker.check(events, pos, rfs) + if (!addToSolver(ocChecker.solver)) return@let SafetyResult.safe(EmptyProof.getInstance()) + + // "Manually" add some conflicts + logger.writeln( + Logger.Level.INFO, + "Auto conflict time (ms): " + + measureTime { + val conflicts = findAutoConflicts(threads, events, rfs, autoConflictConfig, logger) + ocChecker.solver.add(conflicts.map { Not(it.expr) }) + logger.writeln(Logger.Level.INFO, "Auto conflicts: ${conflicts.size}") + } + .inWholeMilliseconds, + ) + + logger.writeln(Logger.Level.MAINSTEP, "Start checking...") + val status: SolverStatus? + val checkerTime = measureTime { status = ocChecker.check(events, pos, rfs, wss) } + if (ocChecker !is XcfaOcCorrectnessValidator) + logger.writeln(Logger.Level.INFO, "Solver time (ms): ${checkerTime.inWholeMilliseconds}") + logger.writeln( + Logger.Level.INFO, + "Propagated clauses: ${ocChecker.getPropagatedClauses().size}", + ) + + ocChecker.solver.statistics.let { + logger.writeln(Logger.Level.INFO, "Solver statistics:") + it.forEach { (k, v) -> logger.writeln(Logger.Level.INFO, "$k: $v") } + } when { - status?.isUnsat == true -> SafetyResult.safe(EmptyProof.getInstance()) - status?.isSat == true -> - SafetyResult.unsafe( - XcfaOcTraceExtractor(xcfa, ocChecker, threads, events, violations, pos).trace, - EmptyProof.getInstance(), - ) + status?.isUnsat == true -> { + if (outputConflictClauses) + System.err.println( + "Conflict clause output time (ms): ${ + measureTime { + ocChecker.getPropagatedClauses().forEach { System.err.println("CC: $it") } + }.inWholeMilliseconds + }" + ) + SafetyResult.safe(EmptyProof.getInstance()) + } + + status?.isSat == true -> { + if (ocChecker is XcfaOcCorrectnessValidator) + return SafetyResult.unsafe(EmptyCex.getInstance(), EmptyProof.getInstance()) + val trace = + XcfaOcTraceExtractor(xcfa, ocChecker, threads, events, violations, pos).trace + SafetyResult.unsafe(trace, EmptyProof.getInstance()) + } else -> SafetyResult.unknown() } } - .also { logger.write(Logger.Level.MAINSTEP, "OC checker result: $it\n") } - - private fun processThread(thread: Thread): List { - threads.add(thread) - val pid = thread.pid - var last = listOf() - var guard = setOf>() - lateinit var lastWrites: MutableMap, Set> - lateinit var edge: XcfaEdge - var inEdge = false - var atomicEntered: Boolean? = null - - val newEvent: (VarDecl<*>, EventType) -> List = { d, type -> + .also { logger.writeln(Logger.Level.MAINSTEP, "OC checker result: $it") } + + private inner class ThreadProcessor(private val thread: Thread) { + + private val pid = thread.pid + private var last = listOf() + private var guard = setOf>() + private lateinit var lastWrites: MutableMap, Set> + private val memoryWrites = mutableSetOf() + private lateinit var edge: XcfaEdge + private var inEdge = false + private var atomicEntered: Boolean? = null + private val multipleUsePidVars = mutableSetOf>() + + fun event(d: VarDecl<*>, type: EventType, varPid: Int? = null): List { check(!inEdge || last.size == 1) - val decl = d.threadVar(pid) + val decl = d.threadVar(varPid ?: pid) val useLastClk = inEdge || atomicEntered == true val e = if (useLastClk) E(decl.getNewIndexed(), type, guard, pid, edge, last.first().clkId) @@ -129,202 +182,276 @@ class XcfaOcChecker( EventType.READ -> lastWrites[decl]?.forEach { rfs.add(RelationType.RF, it, e) } EventType.WRITE -> lastWrites[decl] = setOf(e) } - events[decl] = - (events[decl] ?: mutableMapOf()).apply { - this[pid] = (this[pid] ?: mutableListOf()).apply { add(e) } - } - listOf(e) + events.getOrPut(decl) { mutableMapOf() }.getOrPut(pid) { mutableListOf() }.add(e) + return listOf(e) } - val waitList = mutableSetOf() - val toVisit = - mutableSetOf( - SearchItem(thread.procedure.initLoc).apply { - guards.add(thread.guard) - thread.startEvent?.let { lastEvents.add(it) } - this.lastWrites.add(thread.lastWrites) - } - ) - val threads = mutableListOf() - - while (toVisit.isNotEmpty()) { - val current = toVisit.first() - toVisit.remove(current) - check(current.incoming == current.loc.incomingEdges.size) - check(current.incoming == current.guards.size || current.loc.initial) - // lastEvents intentionally skipped - check(current.incoming == current.lastWrites.size || current.loc.initial) - check(current.incoming == current.threadLookups.size) - check(current.incoming == current.atomics.size) - check(current.atomics.all { it == current.atomics.first() }) // bad pattern otherwise - - if (current.loc.error) { - val errorGuard = Or(current.lastEvents.map { it.guard.toAnd() }) - violations.add(Violation(current.loc, pid, errorGuard, current.lastEvents)) - continue + fun memoryEvent( + deref: Dereference<*, *, *>, + consts: Map>, + type: EventType, + ): List { + check(!inEdge || last.size == 1) + val array = deref.array.with(consts) + val offset = deref.offset.with(consts) + val useLastClk = inEdge || atomicEntered == true + val e = + if (useLastClk) + E(memoryDecl.getNewIndexed(), type, guard, pid, edge, last.first().clkId, array, offset) + else E(memoryDecl.getNewIndexed(), type, guard, pid, edge, array = array, offset = offset) + last.forEach { po(it, e) } + inEdge = true + if (atomicEntered == false) atomicEntered = true + when (type) { + EventType.READ -> memoryWrites.forEach { rfs.add(RelationType.RF, it, e) } + EventType.WRITE -> memoryWrites.add(e) } + events.getOrPut(memoryDecl) { mutableMapOf() }.getOrPut(pid) { mutableListOf() }.add(e) + return listOf(e) + } - if (current.loc.final) { - thread.finalEvents.addAll(current.lastEvents) + fun Expr.toEvents( + consts: Map>? = null, + update: Boolean = true, + ): Map> { + val mutConsts = consts?.toMutableMap() ?: mutableMapOf() + vars.forEach { + last = event(it, EventType.READ) + if (update) mutConsts[it] = last.first().const } + dereferences.forEach { + last = memoryEvent(it, mutConsts, EventType.READ) + if (update) mutConsts[it] = last.first().const + } + return mutConsts + } - val mergedGuard = current.guards.toOrInSet() - val assumeConsts = mutableMapOf, MutableList>>() - - for (e in current.loc.outgoingEdges) { - edge = e - inEdge = false - last = current.lastEvents - // intersection of guards of incoming edges: - guard = mergedGuard - lastWrites = current.lastWrites.merge().toMutableMap() - val threadLookup = - current.threadLookups - .merge { s1, s2 -> - s1 + s2.filter { (guard2, _) -> s1.none { (guard1, _) -> guard1 == guard2 } } - } - .toMutableMap() - var firstLabel = true - atomicEntered = current.atomics.firstOrNull() - - edge.getFlatLabels().forEach { label -> - if (label.references.isNotEmpty() || label.dereferences.isNotEmpty()) { - error("References not supported by OC checker.") + fun process() { + threads.add(thread) + val waitList = mutableSetOf() + val toVisit = + mutableSetOf( + SearchItem(thread.procedure.initLoc).apply { + guards.add(thread.guard) + thread.startEvent?.let { lastEvents.add(it) } + this.lastWrites.add(thread.lastWrites) } - when (label) { - is StmtLabel -> { - when (val stmt = label.stmt) { - is AssignStmt<*> -> { - val consts = mutableMapOf, ConstDecl<*>>() - stmt.expr.vars.forEach { - last = newEvent(it, EventType.READ) - consts[it] = last.first().const + ) + + while (toVisit.isNotEmpty()) { + val current = toVisit.first() + toVisit.remove(current) + check(current.incoming == current.loc.incomingEdges.size) + check(current.incoming == current.guards.size || current.loc.initial) + // lastEvents intentionally skipped + check(current.incoming == current.lastWrites.size || current.loc.initial) + check(current.incoming == current.threadLookups.size) + check(current.incoming == current.atomics.size) + check( + current.atomics.all { it == current.atomics.first() } || + current.loc.error || + (current.loc.outgoingEdges.let { + it.size == 1 && + it.first().let { e -> e.label.getFlatLabels().isEmpty() && e.target.error } + }) + ) + + if (current.loc.error) { + val errorGuard = Or(current.lastEvents.map { it.guard.toAnd() }) + violations.add(Violation(current.loc, pid, errorGuard, current.lastEvents)) + continue + } + + if (current.loc.final) { + thread.finalEvents.addAll(current.lastEvents) + } + + val mergedGuard = current.guards.toOrInSet() + val assumeConsts = mutableMapOf>>() + + for (e in current.loc.outgoingEdges) { + edge = e + inEdge = false + last = current.lastEvents + // intersection of guards of incoming edges: + guard = mergedGuard + lastWrites = current.lastWrites.merge().toMutableMap() + val threadLookup = + current.threadLookups + .merge { s1, s2 -> + s1 + s2.filter { (guard2, _) -> s1.none { (guard1, _) -> guard1 == guard2 } } + } + .toMutableMap() + var firstLabel = true + atomicEntered = current.atomics.firstOrNull() + + edge.getFlatLabels().forEach { label -> + if (label.references.isNotEmpty()) exit("references") + when (label) { + is StmtLabel -> { + when (val stmt = label.stmt) { + is AssignStmt<*> -> { + val consts = stmt.expr.toEvents() + last = event(stmt.varDecl, EventType.WRITE) + last.first().assignment = Eq(last.first().const.ref, stmt.expr.with(consts)) } - last = newEvent(stmt.varDecl, EventType.WRITE) - last.first().assignment = Eq(last.first().const.ref, stmt.expr.withConsts(consts)) - } - is AssumeStmt -> { - val consts = - stmt.cond.vars.associateWith { it.threadVar(pid).getNewIndexed(false) } - val condWithConsts = stmt.cond.withConsts(consts) - val asAssign = - consts.size == 1 && consts.keys.first().threadVar(pid) !in lastWrites - if (edge.source.outgoingEdges.size > 1 || !asAssign) { - guard = guard + condWithConsts - if (firstLabel) { - consts.forEach { (v, c) -> - assumeConsts.getOrPut(v) { mutableListOf() }.add(c) + is AssumeStmt -> { + val consts = + stmt.cond.vars.associateWith { it.threadVar(pid).getNewIndexed(false) } + + stmt.cond.dereferences.associateWith { memoryDecl.getNewIndexed(false) } + val condWithConsts = stmt.cond.with(consts) + val asAssign = + consts.size == 1 && + consts.keys.first().let { it is VarDecl<*> && it !in lastWrites } + if (edge.source.outgoingEdges.size > 1 || !asAssign) { + guard = guard + condWithConsts + if (firstLabel) { + consts.forEach { (v, c) -> + assumeConsts.getOrPut(v) { mutableListOf() }.add(c) + } } } + stmt.cond.toEvents(consts, false) + if (edge.source.outgoingEdges.size == 1 && asAssign) { + last.first().assignment = condWithConsts + } } - stmt.cond.vars.forEach { last = newEvent(it, EventType.READ) } - if (edge.source.outgoingEdges.size == 1 && asAssign) { - last.first().assignment = condWithConsts + + is HavocStmt<*> -> { + last = event(stmt.varDecl, EventType.WRITE) } - } - is HavocStmt<*> -> { - last = newEvent(stmt.varDecl, EventType.WRITE) - } + is MemoryAssignStmt<*, *, *> -> { + val exprConsts = stmt.expr.toEvents() + val arrayConsts = stmt.deref.array.toEvents(exprConsts) + val offsetConsts = stmt.deref.offset.toEvents(arrayConsts) + last = memoryEvent(stmt.deref, arrayConsts + offsetConsts, EventType.WRITE) + last.first().assignment = Eq(last.first().const.ref, stmt.expr.with(exprConsts)) + } - else -> error("Unsupported statement type: $stmt") + else -> exit("unknown statement type: $stmt") + } } - } - is StartLabel -> { - // TODO StartLabel params - if (label.name in thread.startHistory) { - error("Recursive thread start not supported by OC checker.") - } - val procedure = - xcfa.procedures.find { it.name == label.name } - ?: error("Procedure not found: ${label.name}") - last = newEvent(label.pidVar, EventType.WRITE) - val pidVar = label.pidVar.threadVar(pid) - if (this.threads.any { it.pidVar == pidVar }) { - error( - "Using a pthread_t variable in multiple threads is not supported by OC checker." - ) + is StartLabel -> { + if (label.name in thread.startHistory) { + exit("recursive thread start") + } + val procedure = + xcfa.procedures.find { it.name == label.name } + ?: exit("unknown procedure name: ${label.name}") + val newPid = Thread.uniqueId() + + // assign parameter + val consts = label.params[1].toEvents() + val arg = procedure.params.first { it.second != ParamDirection.OUT }.first + last = event(arg, EventType.WRITE, newPid) + last.first().assignment = Eq(last.first().const.ref, label.params[1].with(consts)) + + last = event(label.pidVar, EventType.WRITE) + val pidVar = label.pidVar.threadVar(pid) + if (threads.any { it.pidVar == pidVar }) { + multipleUsePidVars.add(pidVar) + } + val newHistory = thread.startHistory + thread.procedure.name + val newThread = + Thread(newPid, procedure, guard, pidVar, last.first(), newHistory, lastWrites) + last.first().assignment = Eq(last.first().const.ref, Int(newPid)) + threadLookup[pidVar] = setOf(Pair(guard, newThread)) + ThreadProcessor(newThread).process() } - val newHistory = thread.startHistory + thread.procedure.name - val newThread = Thread(procedure, guard, pidVar, last.first(), newHistory, lastWrites) - last.first().assignment = Eq(last.first().const.ref, Int(newThread.pid)) - threadLookup[pidVar] = setOf(Pair(guard, newThread)) - processThread(newThread) - } - is JoinLabel -> { - val incomingGuard = guard - val lastEvents = mutableListOf() - val joinGuards = mutableListOf>>() - threadLookup[label.pidVar.threadVar(pid)]?.forEach { (g, thread) -> - guard = incomingGuard + g + thread.finalEvents.map { it.guard }.toOrInSet() - val joinEvent = newEvent(label.pidVar, EventType.READ).first() - thread.finalEvents.forEach { final -> po(final, joinEvent) } - lastEvents.add(joinEvent) - joinGuards.add(guard) - } ?: error("Thread started in a different thread: not supported by OC checker.") - guard = joinGuards.toOrInSet() - last = lastEvents - } + is JoinLabel -> { + val incomingGuard = guard + val lastEvents = mutableListOf() + val joinGuards = mutableListOf>>() + val pidVar = label.pidVar.threadVar(pid) + if (pidVar in multipleUsePidVars) { + exit("join on a pthread_t variable used in multiple pthread_create calls") + } + threadLookup[pidVar]?.forEach { (g, thread) -> + guard = incomingGuard + g + thread.finalEvents.map { it.guard }.toOrInSet() + val joinEvent = event(label.pidVar, EventType.READ).first() + thread.finalEvents.forEach { final -> po(final, joinEvent) } + lastEvents.add(joinEvent) + joinGuards.add(guard) + thread.joinEvents.add(joinEvent) + } ?: exit("thread started in a different thread") + guard = joinGuards.toOrInSet() + last = lastEvents + } - is FenceLabel -> { - if (label.labels.size > 1 || label.labels.firstOrNull()?.contains("ATOMIC") != true) { - error("Untransformed fence label: $label") + is FenceLabel -> { + if ( + label.labels.size > 1 || label.labels.firstOrNull()?.contains("ATOMIC") != true + ) { + if ( + label.labels.size != 1 || + label.labels.first() != "pthread_exit" || + !edge.target.final + ) { + exit("untransformed fence label: $label") + } + } + if (label.isAtomicBegin) atomicEntered = false + if (label.isAtomicEnd) atomicEntered = null } - if (label.isAtomicBegin) atomicEntered = false - if (label.isAtomicEnd) atomicEntered = null - } - is NopLabel -> {} - else -> error("Unsupported label type by OC checker: $label") + is NopLabel -> {} + else -> exit("unsupported label type: $label") + } + firstLabel = false } - firstLabel = false - } - val searchItem = - waitList.find { it.loc == edge.target } - ?: SearchItem(edge.target).apply { waitList.add(this) } - searchItem.guards.add(guard) - searchItem.lastEvents.addAll(last) - searchItem.lastWrites.add(lastWrites) - searchItem.threadLookups.add(threadLookup) - searchItem.atomics.add(atomicEntered) - searchItem.incoming++ - if (searchItem.incoming == searchItem.loc.incomingEdges.size) { - waitList.remove(searchItem) - toVisit.add(searchItem) + val searchItem = + waitList.find { it.loc == edge.target } + ?: SearchItem(edge.target).apply { waitList.add(this) } + searchItem.guards.add(guard) + searchItem.lastEvents.addAll(last) + searchItem.lastWrites.add(lastWrites) + searchItem.threadLookups.add(threadLookup) + searchItem.atomics.add(atomicEntered) + searchItem.incoming++ + if (searchItem.incoming == searchItem.loc.incomingEdges.size) { + waitList.remove(searchItem) + toVisit.add(searchItem) + } } - } - if (current.loc.outgoingEdges.size > 1) { - for (e in current.loc.outgoingEdges) { - val first = e.getFlatLabels().first() - if (first !is StmtLabel || first.stmt !is AssumeStmt) { - error("Branching with non-assume labels not supported by OC checker.") + if (current.loc.outgoingEdges.size > 1) { + for (e in current.loc.outgoingEdges) { + val first = e.getFlatLabels().first() + if (first !is StmtLabel || first.stmt !is AssumeStmt) { + exit("branching with non-assume labels") + } } - } - assumeConsts.forEach { (_, set) -> - for ((i1, v1) in set.withIndex()) for ((i2, v2) in set.withIndex()) { - if (i1 == i2) break - branchingConditions.add(Eq(v1.ref, v2.ref)) + assumeConsts.forEach { (_, set) -> + for ((i1, v1) in set.withIndex()) for ((i2, v2) in set.withIndex()) { + if (i1 == i2) break + branchingConditions.add(Eq(v1.ref, v2.ref)) + } } } } - } - if (waitList.isNotEmpty()) error("Loops and dangling edges not supported by OC checker.") - return threads + if (waitList.isNotEmpty()) exit("loops and dangling edges") + } } private fun addCrossThreadRelations() { - for ((_, map) in events) for ((pid1, list1) in map) for ((pid2, list2) in map) if (pid1 != pid2) - for (e1 in list1.filter { it.type == EventType.WRITE }) for (e2 in - list2.filter { it.type == EventType.READ }) rfs.add(RelationType.RF, e1, e2) + for ((v, map) in events) { + if (map.values.all { it.all { e -> e.assignment == null } }) + exit("variable $v is not initialized") + for ((pid1, list1) in map) for ((pid2, list2) in map) if (pid1 != pid2) + for (e1 in list1.filter { it.type == EventType.WRITE }) for (e2 in list2) { + if (e2.type == EventType.READ) rfs.add(RelationType.RF, e1, e2) + if (e2.type == EventType.WRITE) wss.add(RelationType.WS, e1, e2) + } + } } - private fun addToSolver(): Boolean { + private fun addToSolver(solver: Solver): Boolean { if (violations.isEmpty()) return false // Value assignment @@ -343,17 +470,18 @@ class XcfaOcChecker( solver.add(Or(violations.map { it.guard })) // RF - rfs.forEach { (_, list) -> + rfs.forEach { (v, list) -> list .groupBy { it.to } .forEach { (event, rels) -> rels.forEach { rel -> - solver.add( - Imply( - rel.declRef, - And(rel.from.guardExpr, rel.to.guardExpr, Eq(rel.from.const.ref, rel.to.const.ref)), - ) - ) // RF-Val + var conseq = + And(rel.from.guardExpr, rel.to.guardExpr, Eq(rel.from.const.ref, rel.to.const.ref)) + if (v == memoryDecl) { + conseq = + And(conseq, Eq(rel.from.array, rel.to.array), Eq(rel.from.offset, rel.to.offset)) + } + solver.add(Imply(rel.declRef, conseq)) // RF-Val } solver.add(Imply(event.guardExpr, Or(rels.map { it.declRef }))) // RF-Some } @@ -386,15 +514,17 @@ class XcfaOcChecker( private fun MutableMap, MutableSet>.add(type: RelationType, from: E, to: E) = getOrPut(from.const.varDecl) { mutableSetOf() }.add(Relation(type, from, to)) - private fun Expr.withConsts(varToConst: Map, ConstDecl<*>>): Expr { - if (this is RefExpr) { - return varToConst[decl]?.ref?.let { cast(it, type) } ?: this + private fun Expr.with(consts: Map>): Expr = + when (this) { + is Dereference<*, *, T> -> consts[this]?.ref?.let { cast(it, type) } ?: this + is RefExpr -> consts[decl]?.ref?.let { cast(it, type) } ?: this + else -> map { it.with(consts) } } - return map { it.withConsts(varToConst) } - } private fun VarDecl.threadVar(pid: Int): VarDecl = - if (xcfa.vars.none { it.wrappedVar == this && !it.threadLocal }) { // if not global var + if ( + this !== memoryDecl && xcfa.vars.none { it.wrappedVar == this && !it.threadLocal } + ) { // if not global var cast( localVars .getOrPut(this) { mutableMapOf() } @@ -408,4 +538,8 @@ class XcfaOcChecker( if (increment) indexing = indexing.inc(this) return constDecl } + + private fun exit(msg: String): Nothing { + error("Feature not supported by OC checker: $msg.") + } } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcCorrectnessValidator.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcCorrectnessValidator.kt new file mode 100644 index 0000000000..1bdfc2bb49 --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcCorrectnessValidator.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.analysis.oc + +import hu.bme.mit.theta.analysis.algorithm.oc.* +import hu.bme.mit.theta.common.logging.Logger +import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.type.booltype.BoolExprs.Not +import hu.bme.mit.theta.solver.Solver +import hu.bme.mit.theta.solver.SolverManager +import hu.bme.mit.theta.solver.SolverStatus +import java.io.File +import kotlin.time.measureTime + +internal class XcfaOcCorrectnessValidator( + decisionProcedure: OcDecisionProcedureType, + private val inputConflictClauseFile: String, + private val threads: Set, + private val permissive: Boolean = true, + private val logger: Logger, +) : OcChecker { + + private var clauseValidationTime = 0L + private lateinit var exactPo: XcfaExactPo + private lateinit var ocChecker: OcChecker + private lateinit var nonOcSolver: Solver + + init { + if (permissive) { + ocChecker = decisionProcedure.checker() + } else { + nonOcSolver = SolverManager.resolveSolverFactory("Z3:4.13").createSolver() + } + } + + override val solver + get() = if (permissive) ocChecker.solver else nonOcSolver + + override fun getRelations() = if (permissive) ocChecker.getRelations() else null + + override fun getPropagatedClauses(): List = + if (permissive) ocChecker.getPropagatedClauses() else listOf() + + override fun check( + events: Map, Map>>, + pos: List>, + rfs: Map, Set>>, + wss: Map, Set>>, + ): SolverStatus? { + val flatRfs = rfs.values.flatten() + val flatEvents = events.values.flatMap { it.values.flatten() } + val parser = XcfaOcReasonParser(flatRfs.toSet(), flatEvents.toSet()) + var parseFailure = 0 + val propagatedClauses: List + logger.writeln( + Logger.Level.INFO, + "Parse time (ms): " + + measureTime { + propagatedClauses = + File(inputConflictClauseFile).readLines().mapNotNull { line -> + try { + parser.parse(line) + } catch (_: Exception) { + parseFailure++ + null + } + } + } + .inWholeMilliseconds, + ) + + clauseValidationTime += measureTime { exactPo = XcfaExactPo(threads) }.inWholeMilliseconds + + val validConflicts: List + clauseValidationTime += + measureTime { validConflicts = propagatedClauses.filter { clause -> checkPath(clause) } } + .inWholeMilliseconds + logger.writeln(Logger.Level.INFO, "Conflict clause parse failures: $parseFailure") + logger.writeln(Logger.Level.INFO, "Parsed conflict clauses: ${propagatedClauses.size}") + logger.writeln(Logger.Level.INFO, "Validated conflict clauses: ${validConflicts.size}") + logger.writeln(Logger.Level.INFO, "Clause validation time (ms): $clauseValidationTime") + + if (permissive) { + ocChecker.solver.add(validConflicts.map { Not(it.expr) }) + } else { + nonOcSolver.add(validConflicts.map { Not(it.expr) }) + } + val result: SolverStatus? + logger.writeln( + Logger.Level.INFO, + "Solver time (ms): " + + measureTime { + result = + if (permissive) { + ocChecker.check(events, pos, rfs, wss) + } else { + nonOcSolver.check() + } + } + .inWholeMilliseconds, + ) + return result + } + + private fun checkPath(combinedReason: Reason, from: E? = null, to: E? = null): Boolean { + val reasons = + combinedReason.reasons.filter { r -> + when (r) { + is RelationReason<*>, + is WriteSerializationReason<*>, + is FromReadReason<*> -> if (r.derivable()) true else return false + + is PoReason -> false + else -> return false + } + } + if (reasons.isEmpty()) return if (from != null) isPo(from, to!!) else false + + var possibleOrders = + if (from == null) { + val clkId = reasons.first().from.clkId + if (reasons.all { it.from.clkId == clkId && it.to.clkId == clkId }) return false + listOf(listOf(reasons.first()) to reasons.slice(1 until reasons.size)) + } else { + reasons.filter { isPo(from, it.from) }.map { listOf(it) to reasons - it } + } + + for (i in 1 until reasons.size) { + val newPossibleOrders = mutableListOf, List>>() + possibleOrders.forEach { po -> + val previous = po.first.last() + po.second + .filter { isPo(previous.to, it.from) } + .forEach { next -> newPossibleOrders.add(po.first + next to po.second - next) } + } + possibleOrders = newPossibleOrders + } + + if (from != null) return possibleOrders.any { isPo(it.first.last().to, to!!) } + return possibleOrders.any { isPo(it.first.last().to, it.first.first().from) } // check cylce + } + + private fun isPo(from: E, to: E): Boolean = exactPo.isPo(from, to) + + private fun isRf(rf: Relation<*>): Boolean = + rf.from.const.varDecl == rf.to.const.varDecl && + rf.from.type == EventType.WRITE && + rf.to.type == EventType.READ + + private fun derivable(rf: Relation<*>, w: Event): Boolean = + isRf(rf) && rf.from.const.varDecl == w.const.varDecl && w.type == EventType.WRITE + + private fun Reason.derivable(): Boolean = + when (this) { + is PoReason -> false + is CombinedReason -> reasons.all { it.derivable() } + is RelationReason<*> -> isRf(this.relation) + is WriteSerializationReason<*> -> { + this as WriteSerializationReason + if (!derivable(rf, w)) false else checkPath(wBeforeRf, w, rf.to) + } + + is FromReadReason<*> -> { + this as FromReadReason + if (!derivable(rf, w)) false else checkPath(wAfterRf, rf.from, w) + } + } + + private val Reason.from: E + get() = + when (this) { + is RelationReason<*> -> (this as RelationReason).relation.from + is WriteSerializationReason<*> -> (this as WriteSerializationReason).w + is FromReadReason<*> -> (this as FromReadReason).rf.to + else -> error("Unsupported reason type.") + } + + private val Reason.to: E + get() = + when (this) { + is RelationReason<*> -> (this as RelationReason).relation.to + is WriteSerializationReason<*> -> (this as WriteSerializationReason).rf.from + is FromReadReason<*> -> (this as FromReadReason).w + else -> error("Unsupported reason type.") + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcPreConflictFinder.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcPreConflictFinder.kt new file mode 100644 index 0000000000..ea7a73252c --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcPreConflictFinder.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.analysis.oc + +import hu.bme.mit.theta.analysis.algorithm.oc.* +import hu.bme.mit.theta.common.logging.Logger +import hu.bme.mit.theta.core.decl.VarDecl + +@Suppress("unused") +enum class AutoConflictFinderConfig { + + NONE, + RF, + RF_WS_FR, +} + +internal fun findAutoConflicts( + threads: Set, + events: Map, Map>>, + rfs: Map, Set>>, + config: AutoConflictFinderConfig, + logger: Logger, +): List { + if (config == AutoConflictFinderConfig.NONE) return emptyList() + val exactPo = XcfaExactPo(threads) + val po = { from: E, to: E -> exactPo.isPo(from, to) } + val flatRfs = rfs.values.flatten().toMutableList() + val conflicts = mutableListOf() + + fun findSimplePath(from: E, to: E): Reason? { + if (from.clkId == to.clkId) { + if (from.id < to.id) return PoReason + return null + } + if (po(from, to)) return PoReason + return flatRfs.find { po(from, it.from) && po(it.to, to) }?.let { RelationReason(it) } + } + + // Cycle of two RF edges (plus po edges) + for (i in 0 until flatRfs.size) { + for (j in i + 1 until flatRfs.size) { + val rf1 = flatRfs[i] + val rf2 = flatRfs[j] + val clkId = rf1.from.clkId + if (rf1.to.clkId == clkId && rf2.from.clkId == clkId && rf2.to.clkId == clkId) continue + if (po(rf1.to, rf2.from) && po(rf2.to, rf1.from)) { + conflicts.add(RelationReason(rf1) and RelationReason(rf2)) + } + } + } + + val rfCnt = conflicts.size + logger.writeln(Logger.Level.INFO, "RF conflicts: $rfCnt") + if (config == AutoConflictFinderConfig.RF) return conflicts + + // Find WS and FR conflicts + rfs.forEach { (v, vRfs) -> + val writes = events[v]?.flatMap { it.value }?.filter { it.type == EventType.WRITE } ?: listOf() + vRfs.forEach { rf -> + writes + .filter { rf.from != it && rf.from.potentialSameMemory(it) } + .forEach { w -> + findSimplePath(w, rf.to)?.let { wRfTo -> + findSimplePath(rf.from, w)?.let { rfFromW -> + conflicts.add(WriteSerializationReason(rf, w, wRfTo) and rfFromW) + conflicts.add(FromReadReason(rf, w, rfFromW) and wRfTo) + } + } + } + } + } + + logger.writeln(Logger.Level.INFO, "WS, FR conflicts (2x): ${conflicts.size - rfCnt}") + return conflicts +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcReasonParser.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcReasonParser.kt new file mode 100644 index 0000000000..9c335af7ff --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcReasonParser.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package hu.bme.mit.theta.xcfa.analysis.oc + +import hu.bme.mit.theta.analysis.algorithm.oc.* + +internal class XcfaOcReasonParser(private val rels: Set, private val events: Set) { + + fun parse(input: String): Reason? { + if (input.startsWith("CC: ")) { + return parseReason(input.substring(4)) + } + return null + } + + private fun parseReason(input: String): Reason { + val i = input.trim() + + if (i == "[]") { + return CombinedReason(emptyList()) + } + + if (i.startsWith("[") && i.endsWith("]")) { + val parts = mutableListOf() + var str = i.substring(1, i.length - 1) + var squareBracketCount = 0 + var j = 0 + while (j < str.length) { + if (str[j] == '[') squareBracketCount++ + if (str[j] == ']') squareBracketCount-- + if (str[j] == ';' && squareBracketCount == 0) { + parts.add(str.substring(0, j)) + str = str.substring(j + 1) + j = 0 + } else { + j++ + } + } + parts.add(str) + return CombinedReason(parts.map { parseReason(it) }) + } + + if (i.startsWith("REL(") && i.endsWith(")")) { + val relStr = i.substring(4, i.length - 1).trim() + val rel = rels.find { it.decl.name == relStr } ?: error("Unknown relation: $relStr") + return RelationReason(rel) + } + + if (i.startsWith("WS(") && i.endsWith(")")) { + val parts = i.substring(3, i.length - 1).split(",").map { it.trim() } + val rfStr = parts[0] + val wStr = parts[1] + val wBeforeRfStr = parts.subList(2, parts.size).joinToString(",") + + val rf = rels.find { it.decl.name == rfStr } ?: error("Unknown relation: $rfStr") + val w = events.find { it.const.name == wStr } ?: error("Unknown event: $wStr") + val wBeforeRf = parseReason(wBeforeRfStr) + return WriteSerializationReason(rf, w, wBeforeRf) + } + + if (i.startsWith("FR(") && i.endsWith(")")) { + val parts = i.substring(3, i.length - 1).split(",").map { it.trim() } + val rfStr = parts[0] + val wStr = parts[1] + val wAfterRfStr = parts.subList(2, parts.size).joinToString(",") + + val rf = rels.find { it.decl.name == rfStr } ?: error("Unknown relation: $rfStr") + val w = events.find { it.const.name == wStr } ?: error("Unknown event: $wStr") + val wAfterRf = parseReason(wAfterRfStr) + return FromReadReason(rf, w, wAfterRf) + } + + if (i == "PO()") return PoReason + error("Unknown reason format: $input") + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTraceExtractor.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTraceExtractor.kt index 05ff67fc9e..50396818a3 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTraceExtractor.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTraceExtractor.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis.oc import hu.bme.mit.theta.analysis.Trace @@ -36,150 +35,178 @@ import hu.bme.mit.theta.xcfa.model.XcfaEdge import hu.bme.mit.theta.xcfa.model.XcfaLocation import java.util.* -/** - * Extracts an error trace from the given model. - */ +/** Extracts an error trace from the given model. */ internal class XcfaOcTraceExtractor( - private val xcfa: XCFA, - private val ocChecker: OcChecker, - private val threads: Set, - private val events: Map, Map>>, - private val violations: List, - private val pos: List + private val xcfa: XCFA, + private val ocChecker: OcChecker, + private val threads: Set, + private val events: Map, Map>>, + private val violations: List, + private val pos: List, ) { - internal val trace: Trace>, XcfaAction> - get() { - check(ocChecker.solver.status.isSat) - val model = ocChecker.solver.model ?: error("No model found for trace extraction.") - val stateList = mutableListOf>>() - val actionList = mutableListOf() - val valuation = model.toMap() - val (eventTrace, violation) = getEventTrace(model) - - val processes = threads.associate { t -> - t.pid to XcfaProcessState( - locs = LinkedList(listOf(t.procedure.initLoc)), varLookup = LinkedList(listOf()) - ) - } - var explState = PtrState(ExplState.of(ImmutableValuation.from(mapOf()))) - stateList.add(XcfaState(xcfa, processes, explState)) - var lastEdge: XcfaEdge = eventTrace[0].edge - - for ((index, event) in eventTrace.withIndex()) { - valuation[event.const]?.let { - val newVal = explState.innerState.`val`.toMap().toMutableMap() - .apply { put(event.const.varDecl, it) } - explState = PtrState(ExplState.of(ImmutableValuation.from(newVal))) - } - - val nextEdge = eventTrace.getOrNull(index + 1)?.edge - if (nextEdge != lastEdge) { - extend(stateList.last(), event.pid, lastEdge.source, - explState.innerState)?.let { (midActions, midStates) -> - actionList.addAll(midActions) - stateList.addAll(midStates) - } - - val state = stateList.last() - actionList.add(XcfaAction(event.pid, lastEdge)) - stateList.add(state.copy(processes = state.processes.toMutableMap().apply { - put( - event.pid, XcfaProcessState( - locs = LinkedList(listOf(lastEdge.target)), - varLookup = LinkedList(emptyList()) - ) - ) - }, sGlobal = explState, mutexes = state.mutexes.update(lastEdge, event.pid))) - lastEdge = nextEdge ?: break - } - } - - if (!stateList.last().processes[violation.pid]!!.locs.peek().error) { - extend(stateList.last(), violation.pid, violation.errorLoc, - explState.innerState)?.let { (midActions, midStates) -> - actionList.addAll(midActions) - stateList.addAll(midStates) - } - } + internal val trace: Trace>, XcfaAction> + get() { + check(ocChecker.solver.status.isSat) + val model = ocChecker.solver.model ?: error("No model found for trace extraction.") + val stateList = mutableListOf>>() + val actionList = mutableListOf() + val valuation = model.toMap() + val (eventTrace, violation) = getEventTrace(model) + + val processes = + threads.associate { t -> + t.pid to + XcfaProcessState( + locs = LinkedList(listOf(t.procedure.initLoc)), + varLookup = LinkedList(listOf()), + ) + } + var explState = PtrState(ExplState.of(ImmutableValuation.from(mapOf()))) + stateList.add(XcfaState(xcfa, processes, explState)) + var lastEdge: XcfaEdge = eventTrace[0].edge + + for ((index, event) in eventTrace.withIndex()) { + valuation[event.const]?.let { + val newVal = + explState.innerState.`val`.toMap().toMutableMap().apply { put(event.const.varDecl, it) } + explState = PtrState(ExplState.of(ImmutableValuation.from(newVal))) + } - return Trace.of(stateList, actionList) + val nextEdge = eventTrace.getOrNull(index + 1)?.edge + if (nextEdge != lastEdge) { + extend(stateList.last(), event.pid, lastEdge.source, explState.innerState)?.let { + (midActions, midStates) -> + actionList.addAll(midActions) + stateList.addAll(midStates) + } + + val state = stateList.last() + actionList.add(XcfaAction(event.pid, lastEdge)) + stateList.add( + state.copy( + processes = + state.processes.toMutableMap().apply { + put( + event.pid, + XcfaProcessState( + locs = LinkedList(listOf(lastEdge.target)), + varLookup = LinkedList(emptyList()), + ), + ) + }, + sGlobal = explState, + mutexes = state.mutexes.update(lastEdge, event.pid), + ) + ) + lastEdge = nextEdge ?: break } + } - private fun getEventTrace(model: Valuation): Pair, Violation> { - val valuation = model.toMap() - val violation = violations.first { (it.guard.eval(model) as BoolLitExpr).value } - - val relations = ocChecker.getRelations()!! - val reverseRelations = Array(relations.size) { i -> Array(relations.size) { j -> relations[j][i] } } - val eventsByClk = events.values.flatMap { it.values.flatten() }.groupBy { it.clkId } - - val lastEvents = violation.lastEvents.filter { it.enabled(model) == true }.toMutableList() - val finished = mutableListOf() // topological order - while (lastEvents.isNotEmpty()) { // DFS from startEvents as root nodes - val stack = Stack() - stack.push(StackItem(lastEvents.removeFirst())) - while (stack.isNotEmpty()) { - val top = stack.peek() - if (top.eventsToVisit == null) { - val previous = reverseRelations[top.event.clkId].flatMapIndexed { i, r -> - if (r == null) listOf() - else eventsByClk[i] ?: listOf() - }.filter { it.enabled(model) == true } union pos.filter { - it.to == top.event && it.enabled(valuation) == true && it.from.enabled(model) == true - }.map { it.from } - top.eventsToVisit = previous.toMutableList() - } + if (!stateList.last().processes[violation.pid]!!.locs.peek().error) { + extend(stateList.last(), violation.pid, violation.errorLoc, explState.innerState)?.let { + (midActions, midStates) -> + actionList.addAll(midActions) + stateList.addAll(midStates) + } + } - if (top.eventsToVisit!!.isEmpty()) { - stack.pop() - finished.add(top.event) - continue - } + return Trace.of(stateList, actionList) + } - val visiting = top.eventsToVisit!!.find { it.clkId == top.event.clkId } ?: top.eventsToVisit!!.first() - top.eventsToVisit!!.remove(visiting) - if (visiting !in finished) { - stack.push(StackItem(visiting)) + private fun getEventTrace(model: Valuation): Pair, Violation> { + val valuation = model.toMap() + val violation = violations.first { (it.guard.eval(model) as BoolLitExpr).value } + + val relations = ocChecker.getRelations()!! + val reverseRelations = + Array(relations.size) { i -> Array(relations.size) { j -> relations[j][i] } } + val eventsByClk = events.values.flatMap { it.values.flatten() }.groupBy { it.clkId } + + val lastEvents = violation.lastEvents.filter { it.enabled(model) == true }.toMutableList() + val finished = mutableListOf() // topological order + while (lastEvents.isNotEmpty()) { // DFS from startEvents as root nodes + val stack = Stack() + stack.push(StackItem(lastEvents.removeFirst())) + while (stack.isNotEmpty()) { + val top = stack.peek() + if (top.eventsToVisit == null) { + val previous = + reverseRelations[top.event.clkId] + .flatMapIndexed { i, r -> if (r == null) listOf() else eventsByClk[i] ?: listOf() } + .filter { it.enabled(model) == true } union + pos + .filter { + it.to == top.event && + it.enabled(valuation) == true && + it.from.enabled(model) == true } - } + .map { it.from } + top.eventsToVisit = previous.toMutableList() } - return finished to violation - } - private fun extend( - state: XcfaState>, pid: Int, - to: XcfaLocation, explState: ExplState - ): Pair, List>>>? { - val actions = mutableListOf() - val states = mutableListOf>>() - var currentState = state - - // extend the trace until the target location is reached - while (currentState.mutexes[""]?.equals(pid) == false || currentState.processes[pid]!!.locs.peek() != to) { - val stepPid = currentState.mutexes[""] ?: pid // finish atomic block first - val edge = currentState.processes[stepPid]!!.locs.peek().outgoingEdges.firstOrNull() ?: return null - actions.add(XcfaAction(stepPid, edge)) - currentState = currentState.copy(processes = currentState.processes.toMutableMap().apply { - put( - stepPid, XcfaProcessState( - locs = LinkedList(listOf(edge.target)), - varLookup = LinkedList(emptyList()) - ) - ) - }, sGlobal = PtrState(explState), mutexes = currentState.mutexes.update(edge, stepPid)) - states.add(currentState) + if (top.eventsToVisit!!.isEmpty()) { + stack.pop() + finished.add(top.event) + continue } - return actions to states - } - private fun Map.update(edge: XcfaEdge, pid: Int): Map { - val map = this.toMutableMap() - edge.getFlatLabels().forEach { - if (it.isAtomicBegin) map[""] = pid - if (it.isAtomicEnd) map.remove("") + val visiting = + top.eventsToVisit!!.find { it.clkId == top.event.clkId } ?: top.eventsToVisit!!.first() + top.eventsToVisit!!.remove(visiting) + if (visiting !in finished) { + stack.push(StackItem(visiting)) } - return map + } } - -} \ No newline at end of file + return finished to violation + } + + private fun extend( + state: XcfaState>, + pid: Int, + to: XcfaLocation, + explState: ExplState, + ): Pair, List>>>? { + val actions = mutableListOf() + val states = mutableListOf>>() + var currentState = state + + // extend the trace until the target location is reached + while ( + currentState.mutexes[""]?.equals(pid) == false || + currentState.processes[pid]!!.locs.peek() != to + ) { + val stepPid = currentState.mutexes[""] ?: pid // finish atomic block first + val edge = + currentState.processes[stepPid]!!.locs.peek().outgoingEdges.firstOrNull() ?: return null + actions.add(XcfaAction(stepPid, edge)) + currentState = + currentState.copy( + processes = + currentState.processes.toMutableMap().apply { + put( + stepPid, + XcfaProcessState( + locs = LinkedList(listOf(edge.target)), + varLookup = LinkedList(emptyList()), + ), + ) + }, + sGlobal = PtrState(explState), + mutexes = currentState.mutexes.update(edge, stepPid), + ) + states.add(currentState) + } + return actions to states + } + + private fun Map.update(edge: XcfaEdge, pid: Int): Map { + val map = this.toMutableMap() + edge.getFlatLabels().forEach { + if (it.isAtomicBegin) map[""] = pid + if (it.isAtomicEnd) map.remove("") + } + return map + } +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTypes.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTypes.kt index 19858f5a03..1547960467 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTypes.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTypes.kt @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.analysis.oc import hu.bme.mit.theta.analysis.algorithm.oc.* import hu.bme.mit.theta.core.decl.IndexedConstDecl import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.model.ImmutableValuation +import hu.bme.mit.theta.core.model.Valuation import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.LitExpr +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq import hu.bme.mit.theta.core.type.booltype.BoolExprs import hu.bme.mit.theta.core.type.booltype.BoolExprs.And import hu.bme.mit.theta.core.type.booltype.BoolExprs.Or @@ -29,87 +32,167 @@ import hu.bme.mit.theta.xcfa.model.XcfaLocation import hu.bme.mit.theta.xcfa.model.XcfaProcedure internal typealias E = XcfaEvent + internal typealias R = Relation @Suppress("unused") enum class OcDecisionProcedureType(internal val checker: () -> OcChecker) { - BASIC({ BasicOcChecker() }), - PROPAGATOR({ UserPropagatorOcChecker() }), - PREVENTIVE({ PreventivePropagatorOcChecker() }), + BASIC({ BasicOcChecker() }), + PROPAGATOR({ UserPropagatorOcChecker() }), } -/** - * Important! Empty collection is converted to true (not false). - */ -internal fun Collection>.toAnd(): Expr = when (size) { +/** Important! Empty collection is converted to true (not false). */ +internal fun Collection>.toAnd(): Expr = + when (size) { 0 -> BoolExprs.True() 1 -> first() else -> And(this) -} + } /** - * Takes the OR of the contained lists mapped to an AND expression. Simplifications are made based on the list sizes. + * Takes the OR of the contained lists mapped to an AND expression. Simplifications are made based + * on the list sizes. */ -internal fun Collection>>.toOrInSet(): Set> = when (size) { +internal fun Collection>>.toOrInSet(): Set> = + when (size) { 0 -> setOf() 1 -> first() else -> setOf(Or(map { it.toAnd() })) -} + } internal class XcfaEvent( - const: IndexedConstDecl<*>, - type: EventType, - guard: Set>, - pid: Int, - val edge: XcfaEdge, - clkId: Int = uniqueId() + const: IndexedConstDecl<*>, + type: EventType, + guard: Set>, + pid: Int, + val edge: XcfaEdge, + clkId: Int = uniqueClkId(), + val array: Expr<*>? = null, + val offset: Expr<*>? = null, + val id: Int = uniqueId(), ) : Event(const, type, guard, pid, clkId) { - companion object { + private var arrayStatic: LitExpr<*>? = null + private var offsetStatic: LitExpr<*>? = null + private var arrayLit: LitExpr<*>? = null + private var offsetLit: LitExpr<*>? = null - private var cnt: Int = 0 - private fun uniqueId(): Int = cnt++ + init { + check((array == null && offset == null) || (array != null && offset != null)) { + "Array and offset expressions must be both null or both non-null." + } + arrayStatic = tryOrNull { array?.eval(ImmutableValuation.empty()) } + offsetStatic = tryOrNull { offset?.eval(ImmutableValuation.empty()) } + } + + companion object { + + private var idCnt: Int = 0 + private var clkCnt: Int = 0 + + private fun uniqueId(): Int = idCnt++ + + private fun uniqueClkId(): Int = clkCnt++ + } + + // A (memory) event is only considered enabled if the array and offset expressions are also known + // values + override fun enabled(valuation: Valuation): Boolean? { + when (val e = super.enabled(valuation)) { + null, + false -> return e + true -> { + if (array != null) { + arrayLit = tryOrNull { array.eval(valuation) } + if (arrayLit == null) enabled = null + } + if (offset != null) { + offsetLit = tryOrNull { offset.eval(valuation) } + if (offsetLit == null) enabled = null + } + return enabled + } } + } + + override fun sameMemory(other: Event): Boolean { + other as XcfaEvent + if (arrayLit != other.arrayLit) return false + if (offsetLit != other.offsetLit) return false + return potentialSameMemory(other) + } + + fun potentialSameMemory(other: XcfaEvent): Boolean { + if (!super.sameMemory(other)) return false + if (arrayStatic != null && other.arrayStatic != null && arrayStatic != other.arrayStatic) + return false + if (offsetStatic != null && other.offsetStatic != null && offsetStatic != other.offsetStatic) + return false + return true + } + + override fun interferenceCond(other: Event): Expr? { + other as XcfaEvent + array ?: return null + other.array ?: return null + + var arrayEq: Expr? = Eq(array, other.array) + if (arrayStatic != null && other.arrayStatic != null) { + if (arrayStatic != other.arrayStatic) return null + arrayEq = null + } + + var offsetEq: Expr? = Eq(offset, other.offset) + if (offsetStatic != null && other.offsetStatic != null) { + if (offsetStatic != other.offsetStatic) return null + offsetEq = null + } + + return listOfNotNull(arrayEq, offsetEq).toAnd() + } } internal data class Violation( - val errorLoc: XcfaLocation, - val pid: Int, - val guard: Expr, - val lastEvents: List, + val errorLoc: XcfaLocation, + val pid: Int, + val guard: Expr, + val lastEvents: List, ) internal data class Thread( - val procedure: XcfaProcedure, - val guard: Set> = setOf(), - val pidVar: VarDecl<*>? = null, - val startEvent: XcfaEvent? = null, - val startHistory: List = listOf(), - val lastWrites: Map, Set> = mapOf(), - val pid: Int = uniqueId(), + val pid: Int = uniqueId(), + val procedure: XcfaProcedure, + val guard: Set> = setOf(), + val pidVar: VarDecl<*>? = null, + val startEvent: XcfaEvent? = null, + val startHistory: List = listOf(), + val lastWrites: Map, Set> = mapOf(), + val joinEvents: MutableSet = mutableSetOf(), ) { - val finalEvents: MutableSet = mutableSetOf() + val finalEvents: MutableSet = mutableSetOf() - companion object { + companion object { - private var cnt: Int = 0 - private fun uniqueId(): Int = cnt++ - } + private var cnt: Int = 0 + + fun uniqueId(): Int = cnt++ + } } internal data class SearchItem(val loc: XcfaLocation) { - val guards: MutableList>> = mutableListOf() - val lastEvents: MutableList = mutableListOf() - val lastWrites: MutableList, Set>> = mutableListOf() - val threadLookups: MutableList, Set>, Thread>>>> = mutableListOf() - val atomics: MutableList = mutableListOf() - var incoming: Int = 0 + val guards: MutableList>> = mutableListOf() + val lastEvents: MutableList = mutableListOf() + val lastWrites: MutableList, Set>> = mutableListOf() + val threadLookups: MutableList, Set>, Thread>>>> = + mutableListOf() + val atomics: MutableList = mutableListOf() + var incoming: Int = 0 } internal data class StackItem(val event: XcfaEvent) { - var eventsToVisit: MutableList? = null + var eventsToVisit: MutableList? = null } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaDporLts.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaDporLts.kt index d036c61ecd..5ee85c07d3 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaDporLts.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaDporLts.kt @@ -35,433 +35,428 @@ import java.util.stream.Stream import kotlin.math.max import kotlin.random.Random -/** - * Type definitions for states, actions and ARG nodes. - */ +/** Type definitions for states, actions and ARG nodes. */ private typealias S = XcfaState> + private typealias A = XcfaAction + private typealias Node = ArgNode -/** - * Backtrack set of a state: actions to be explored when backtracking in DFS. - */ +/** Backtrack set of a state: actions to be explored when backtracking in DFS. */ private var State.backtrack: MutableSet by extension() -/** - * Sleep set of a state: actions that need not be explored. - */ +/** Sleep set of a state: actions that need not be explored. */ private var State.sleep: MutableSet by extension() -/** - * Set of explored actions from a state. - */ +/** Set of explored actions from a state. */ private var State.explored: MutableSet by extension() -/** - * Reexplored actions in a new CEGAR iteration (only relevant when lazy pruning is used). - */ +/** Reexplored actions in a new CEGAR iteration (only relevant when lazy pruning is used). */ private val reExploredDelegate = nullableExtension() private var State.reExplored: Boolean? by reExploredDelegate -/** - * Explored actions from an ARG node. - */ -private val Node.explored: Set get() = outEdges.map { it.action }.collect(Collectors.toSet()) +/** Explored actions from an ARG node. */ +private val Node.explored: Set + get() = outEdges.map { it.action }.collect(Collectors.toSet()) /** - * An LTS implementing a dynamic partial order reduction algorithm (Source-DPOR) for state space exploration. - * @see Source Sets: A Foundation for Optimal Dynamic Partial Order Reduction + * An LTS implementing a dynamic partial order reduction algorithm (Source-DPOR) for state space + * exploration. + * + * @see Source Sets: A Foundation for Optimal Dynamic + * Partial Order Reduction */ open class XcfaDporLts(private val xcfa: XCFA) : LTS { - companion object { - - var random: Random = - Random.Default // use Random(seed) with a seed or Random.Default without seed - - /** - * Simple LTS that returns the enabled actions in a state. - */ - private val simpleXcfaLts = getXcfaLts() - - /** - * The enabled actions of a state. - */ - private val State.enabled: Collection - get() = simpleXcfaLts.getEnabledActionsFor(this as S) - - /** - * Partial order of states considering sleep sets (unexplored behavior). - */ - fun getPartialOrder(partialOrd: PartialOrd) = PartialOrd { s1, s2 -> - partialOrd.isLeq( - s1, s2 - ) && s2.reExplored == true && s1.sleep.containsAll(s2.sleep - s2.explored) - } - } - - /** - * Represents an element of the DFS search stack. - */ - private data class StackItem( - val node: Node, // the ARG node - val processLastAction: Map = mutableMapOf(), // the index of the last actions of processes in the search stack - val lastDependents: Map> = mutableMapOf(), // for each process p it stores the index of the last action of each process that is dependent with the last actions of p - val mutexLocks: MutableMap = mutableMapOf(), // for each locked mutex the index of the state on the stack where the mutex has been locked - private val _backtrack: MutableSet = mutableSetOf(), - private val _sleep: MutableSet = mutableSetOf(), - ) { - - val action: A get() = node.inEdge.get().action // the incoming action to the current state - - val state: S get() = node.state // the current state of this stack item - - var backtrack: MutableSet by node.state::backtrack // backtrack set of the current state - - var sleep: MutableSet by node.state::sleep // sleep set of the current state - private set - - var explored: MutableSet by node.state::explored // explored actions from the current state - private set - - init { - backtrack = _backtrack - sleep = _sleep - explored = mutableSetOf() - } - - override fun toString() = action.toString() + companion object { + + var random: Random = + Random.Default // use Random(seed) with a seed or Random.Default without seed + + /** Simple LTS that returns the enabled actions in a state. */ + private val simpleXcfaLts = getXcfaLts() + + /** The enabled actions of a state. */ + private val State.enabled: Collection + get() = simpleXcfaLts.getEnabledActionsFor(this as S) + + /** Partial order of states considering sleep sets (unexplored behavior). */ + fun getPartialOrder(partialOrd: PartialOrd) = + PartialOrd { s1, s2 -> + partialOrd.isLeq(s1, s2) && + s2.reExplored == true && + s1.sleep.containsAll(s2.sleep - s2.explored) + } + } + + /** Represents an element of the DFS search stack. */ + private data class StackItem( + val node: Node, // the ARG node + val processLastAction: Map = + mutableMapOf(), // the index of the last actions of processes in the search stack + val lastDependents: Map> = + mutableMapOf(), // for each process p it stores the index of the last action of each process + // that is dependent with the last actions of p + val mutexLocks: MutableMap = + mutableMapOf(), // for each locked mutex the index of the state on the stack where the mutex + // has been locked + private val _backtrack: MutableSet = mutableSetOf(), + private val _sleep: MutableSet = mutableSetOf(), + ) { + + val action: A + get() = node.inEdge.get().action // the incoming action to the current state + + val state: S + get() = node.state // the current state of this stack item + + var backtrack: MutableSet by node.state::backtrack // backtrack set of the current state + + var sleep: MutableSet by node.state::sleep // sleep set of the current state + private set + + var explored: MutableSet by node.state::explored // explored actions from the current state + private set + + init { + backtrack = _backtrack + sleep = _sleep + explored = mutableSetOf() } - private val stack: Stack = Stack() // the DFS search stack - - private val last get() = stack.peek() // the top item of the search stack - - /** - * Returns an action (wrapped into a set) to be explored from the given state. - */ - override fun getEnabledActionsFor(state: S): Set { - require(state == last.state) - val possibleActions = last.backtrack subtract last.sleep - if (possibleActions.isEmpty()) return emptySet() - - val actionToExplore = possibleActions.random(random) - last.backtrack.addAll(state.enabled.filter { it.pid == actionToExplore.pid }) - last.sleep.add(actionToExplore) - last.explored.add(actionToExplore) - return setOf(actionToExplore) - } - - /** - * A waitlist implementation that controls the search: from which state do we need to explore, pops states from the search stack. - */ - val waitlist = object : Waitlist { - - /** - * Adds a new ARG node to the search stack. - */ - override fun add(item: Node) { - var node = item - // lazy pruning: goes to the root when the stack is empty - while (stack.isEmpty() && node.parent.isPresent) node = node.parent.get() - - node.state.reExplored = - true // lazy pruning: indicates that the state is explored in the current iteration - push(node, stack.size) + override fun toString() = action.toString() + } + + private val stack: Stack = Stack() // the DFS search stack + + private val last + get() = stack.peek() // the top item of the search stack + + /** Returns an action (wrapped into a set) to be explored from the given state. */ + override fun getEnabledActionsFor(state: S): Set { + require(state == last.state) + val possibleActions = last.backtrack subtract last.sleep + if (possibleActions.isEmpty()) return emptySet() + + val actionToExplore = possibleActions.random(random) + last.backtrack.addAll(state.enabled.filter { it.pid == actionToExplore.pid }) + last.sleep.add(actionToExplore) + last.explored.add(actionToExplore) + return setOf(actionToExplore) + } + + /** + * A waitlist implementation that controls the search: from which state do we need to explore, + * pops states from the search stack. + */ + val waitlist = + object : Waitlist { + + /** Adds a new ARG node to the search stack. */ + override fun add(item: Node) { + var node = item + // lazy pruning: goes to the root when the stack is empty + while (stack.isEmpty() && node.parent.isPresent) node = node.parent.get() + + node.state.reExplored = + true // lazy pruning: indicates that the state is explored in the current iteration + push(node, stack.size) + } + + override fun addAll(items: Collection) { + require(items.size <= 1) + if (items.isNotEmpty()) add(items.first()) + } + + override fun addAll(items: Stream) { + val iterator = items.iterator() + if (iterator.hasNext()) add(iterator.next()) + require(!iterator.hasNext()) + } + + /** + * Returns true if there is no more actions to explore. States are popped from the stack while + * there is no more action to be explored. + */ + override fun isEmpty(): Boolean { + if (last.node.isCovered && last.node.isFeasible) { + // if the last node is covered, the subtree of the covering node is explored again (to + // detect possible races) + virtualExploration(last.node.coveringNode.get(), stack.size) + } else { + // when lazy pruning is used the explored parts from previous iterations are reexplored to + // detect possible races + exploreLazily() } - - override fun addAll(items: Collection) { - require(items.size <= 1) - if (items.isNotEmpty()) add(items.first()) + while ( + stack.isNotEmpty() && + (last.node.isSubsumed || + (last.node.isExpanded && last.backtrack.subtract(last.sleep).isEmpty())) + ) { // until we need to pop (the last is covered or not feasible, or it has no more actions + // that need to be explored + if (stack.size >= 2) { + val lastButOne = stack[stack.size - 2] + val mutexNeverReleased = + last.mutexLocks.containsKey("") && + (last.state.mutexes.keys subtract lastButOne.state.mutexes.keys).contains("") + if (last.node.explored.isEmpty() || mutexNeverReleased) { + // if a mutex is never released another action (practically all the others) have to be + // explored + lastButOne.backtrack = lastButOne.state.enabled.toMutableSet() + } + } + stack.pop() + exploreLazily() } - - override fun addAll(items: Stream) { - val iterator = items.iterator() - if (iterator.hasNext()) add(iterator.next()) - require(!iterator.hasNext()) + return stack.isEmpty() + } + + /** Returns the last element of the stack after all unnecessary actions have been removed. */ + override fun remove(): Node { + if (isEmpty) throw NoSuchElementException("The search stack is empty.") + return last.node + } + + override fun size() = stack.size + + /** Clears the search stack and the registry of reexplored actions needed for lazy pruning. */ + override fun clear() { + stack.clear() + reExploredDelegate.clear() + } + + /** Pushes an item to the search stack. */ + private fun push(item: Node, virtualLimit: Int): Boolean { + if (!item.inEdge.isPresent) { // the first item is simply put on the stack + stack.push(StackItem(item, _backtrack = item.state.enabled.toMutableSet())) + return true } - /** - * Returns true if there is no more actions to explore. States are popped from the stack while there is no more action to be explored. - */ - override fun isEmpty(): Boolean { - if (last.node.isCovered && last.node.isFeasible) { - // if the last node is covered, the subtree of the covering node is explored again (to detect possible races) - virtualExploration(last.node.coveringNode.get(), stack.size) - } else { - // when lazy pruning is used the explored parts from previous iterations are reexplored to detect possible races - exploreLazily() - } - while (stack.isNotEmpty() && (last.node.isSubsumed || (last.node.isExpanded && last.backtrack.subtract( - last.sleep - ).isEmpty())) - ) { // until we need to pop (the last is covered or not feasible, or it has no more actions that need to be explored - if (stack.size >= 2) { - val lastButOne = stack[stack.size - 2] - val mutexNeverReleased = - last.mutexLocks.containsKey( - "") && (last.state.mutexes.keys subtract lastButOne.state.mutexes.keys).contains( - "" - ) - if (last.node.explored.isEmpty() || mutexNeverReleased) { - // if a mutex is never released another action (practically all the others) have to be explored - lastButOne.backtrack = lastButOne.state.enabled.toMutableSet() - } + val newaction = item.inEdge.get().action + val process = newaction.pid + + val newProcessLastAction = + last.processLastAction.toMutableMap().apply { this[process] = stack.size } + var newLastDependents = + (last.lastDependents[process]?.toMutableMap() ?: mutableMapOf()).apply { + this[process] = stack.size + } + val relevantProcesses = (newProcessLastAction.keys - setOf(process)).toMutableSet() + + // Race detection + for (index in stack.size - 1 downTo 1) { + if (relevantProcesses.isEmpty()) break + val node = stack[index].node + + val action = node.inEdge.get().action + if (relevantProcesses.contains(action.pid)) { + if ( + newLastDependents.containsKey(action.pid) && + index <= checkNotNull(newLastDependents[action.pid]) + ) { + // there is an action a' such that action -> a' -> newaction (->: happens-before) + relevantProcesses.remove(action.pid) + } else if (dependent(newaction, action)) { + // reversible race + val v = notdep(index, newaction) + val iv = initials(index - 1, v) + if (iv.isEmpty()) continue // due to mutex (e.g. atomic block) + + if (index < virtualLimit) { + // only add new action to backtrack sets in the "real" part of the stack + val backtrack = stack[index - 1].backtrack + if ((iv intersect backtrack).isEmpty()) { + backtrack.add(iv.random(random)) } - stack.pop() - exploreLazily() - } - return stack.isEmpty() - } + } - /** - * Returns the last element of the stack after all unnecessary actions have been removed. - */ - override fun remove(): Node { - if (isEmpty) throw NoSuchElementException("The search stack is empty.") - return last.node + newLastDependents[action.pid] = index + newLastDependents = + max(newLastDependents, checkNotNull(stack[index].lastDependents[action.pid])) + relevantProcesses.remove(action.pid) + } + } } - override fun size() = stack.size - - /** - * Clears the search stack and the registry of reexplored actions needed for lazy pruning. - */ - override fun clear() { - stack.clear() - reExploredDelegate.clear() + // Set properties of new stack item + val newProcesses = item.state.processes.keys subtract last.state.processes.keys + + val oldMutexes = last.state.mutexes.keys + val newMutexes = item.state.mutexes.keys + val lockedMutexes = newMutexes - oldMutexes + val releasedMutexes = oldMutexes - newMutexes + if (!item.state.isBottom) { + releasedMutexes.forEach { m -> + last.mutexLocks[m]?.let { stack[it].mutexLocks.remove(m) } + } } - /** - * Pushes an item to the search stack. - */ - private fun push(item: Node, virtualLimit: Int): Boolean { - if (!item.inEdge.isPresent) { // the first item is simply put on the stack - stack.push(StackItem(item, _backtrack = item.state.enabled.toMutableSet())) - return true - } - - val newaction = item.inEdge.get().action - val process = newaction.pid - - val newProcessLastAction = - last.processLastAction.toMutableMap().apply { this[process] = stack.size } - var newLastDependents = - (last.lastDependents[process]?.toMutableMap() ?: mutableMapOf()).apply { - this[process] = stack.size - } - val relevantProcesses = (newProcessLastAction.keys - setOf(process)).toMutableSet() - - // Race detection - for (index in stack.size - 1 downTo 1) { - if (relevantProcesses.isEmpty()) break - val node = stack[index].node - - val action = node.inEdge.get().action - if (relevantProcesses.contains(action.pid)) { - if (newLastDependents.containsKey(action.pid) && index <= checkNotNull( - newLastDependents[action.pid])) { - // there is an action a' such that action -> a' -> newaction (->: happens-before) - relevantProcesses.remove(action.pid) - } else if (dependent(newaction, action)) { - // reversible race - val v = notdep(index, newaction) - val iv = initials(index - 1, v) - if (iv.isEmpty()) continue // due to mutex (e.g. atomic block) - - if (index < virtualLimit) { - // only add new action to backtrack sets in the "real" part of the stack - val backtrack = stack[index - 1].backtrack - if ((iv intersect backtrack).isEmpty()) { - backtrack.add(iv.random(random)) - } - } - - newLastDependents[action.pid] = index - newLastDependents = - max(newLastDependents, checkNotNull(stack[index].lastDependents[action.pid])) - relevantProcesses.remove(action.pid) - } + val isVirtualExploration = virtualLimit < stack.size || item.parent.get() != last.node + val newSleep = + if (isVirtualExploration) { + item.state.sleep + } else { + last.sleep.filter { !dependent(it, newaction) }.toMutableSet() + } + val enabledActions = item.state.enabled subtract newSleep + val newBacktrack = + when { + isVirtualExploration -> + item.state.backtrack // for virtual exploration through a covering relation + item.explored.isNotEmpty() -> + item.explored.toMutableSet().apply { + if (newSleep.containsAll(this) && enabledActions.isNotEmpty()) { + this.add(enabledActions.random(random)) } + } // for LAZY pruning + enabledActions.isNotEmpty() -> mutableSetOf(enabledActions.random(random)) + else -> mutableSetOf() + } + + // Check cycle before pushing new item on stack + stack + .indexOfFirst { it.node == item } + .let { + if (it != -1) { + if (it < virtualLimit) { + stack[it].backtrack = stack[it].state.enabled.toMutableSet() + } + return false } - - // Set properties of new stack item - val newProcesses = item.state.processes.keys subtract last.state.processes.keys - - val oldMutexes = last.state.mutexes.keys - val newMutexes = item.state.mutexes.keys - val lockedMutexes = newMutexes - oldMutexes - val releasedMutexes = oldMutexes - newMutexes - if (!item.state.isBottom) { - releasedMutexes.forEach { m -> - last.mutexLocks[m]?.let { - stack[it].mutexLocks.remove( - m - ) - } - } - } - - val isVirtualExploration = virtualLimit < stack.size || item.parent.get() != last.node - val newSleep = if (isVirtualExploration) { - item.state.sleep - } else { - last.sleep.filter { !dependent(it, newaction) }.toMutableSet() - } - val enabledActions = item.state.enabled subtract newSleep - val newBacktrack = when { - isVirtualExploration -> item.state.backtrack // for virtual exploration through a covering relation - item.explored.isNotEmpty() -> item.explored.toMutableSet().apply { - if (newSleep.containsAll(this) && enabledActions.isNotEmpty()) { - this.add(enabledActions.random(random)) - } - } // for LAZY pruning - enabledActions.isNotEmpty() -> mutableSetOf(enabledActions.random(random)) - else -> mutableSetOf() - } - - // Check cycle before pushing new item on stack - stack.indexOfFirst { it.node == item }.let { - if (it != -1) { - if (it < virtualLimit) { - stack[it].backtrack = stack[it].state.enabled.toMutableSet() - } - return false + } + + // Push new item on the stack + stack.push( + StackItem( + node = item, + processLastAction = newProcessLastAction, + lastDependents = + last.lastDependents.toMutableMap().apply { + this[process] = newLastDependents + newProcesses.forEach { + this[it] = max(this[it] ?: mutableMapOf(), newLastDependents) } - } - - // Push new item on the stack - stack.push( - StackItem( - node = item, - processLastAction = newProcessLastAction, - lastDependents = last.lastDependents.toMutableMap().apply { - this[process] = newLastDependents - newProcesses.forEach { - this[it] = max(this[it] ?: mutableMapOf(), newLastDependents) - } - }, - mutexLocks = last.mutexLocks.toMutableMap().apply { - lockedMutexes.forEach { this[it] = stack.size } - releasedMutexes.forEach(::remove) - }, - _backtrack = newBacktrack, - _sleep = newSleep, - ) - ) - return true + }, + mutexLocks = + last.mutexLocks.toMutableMap().apply { + lockedMutexes.forEach { this[it] = stack.size } + releasedMutexes.forEach(::remove) + }, + _backtrack = newBacktrack, + _sleep = newSleep, + ) + ) + return true + } + + /** Virtually reexplores part of the ARG for race detection. Used when a node is covered. */ + private fun virtualExploration(node: Node, realStackSize: Int) { + val startStackSize = stack.size + val virtualStack = Stack() + virtualStack.push(node) + while (virtualStack.isNotEmpty()) { + val visiting = virtualStack.pop() + while (stack.size > startStackSize && stack.peek().node != visiting.parent.get()) stack + .pop() + + if (node != visiting) { + if (!push(visiting, startStackSize) || noInfluenceOnRealExploration(realStackSize)) + continue + } + + // visiting is not on the stack: no cycle && further virtual exploration can influence + // real exploration + if (visiting.isCovered) { + val coveringNode = visiting.coveringNode.get() + virtualExploration(coveringNode, realStackSize) + } else { + visiting.outEdges.forEach { virtualStack.push(it.target) } + } } - - /** - * Virtually reexplores part of the ARG for race detection. Used when a node is covered. - */ - private fun virtualExploration(node: Node, realStackSize: Int) { - val startStackSize = stack.size - val virtualStack = Stack() - virtualStack.push(node) - while (virtualStack.isNotEmpty()) { - val visiting = virtualStack.pop() - while (stack.size > startStackSize && stack.peek().node != visiting.parent.get()) stack.pop() - - if (node != visiting) { - if (!push(visiting, startStackSize) || noInfluenceOnRealExploration( - realStackSize - ) - ) continue - } - - // visiting is not on the stack: no cycle && further virtual exploration can influence real exploration - if (visiting.isCovered) { - val coveringNode = visiting.coveringNode.get() - virtualExploration(coveringNode, realStackSize) - } else { - visiting.outEdges.forEach { virtualStack.push(it.target) } - } - } - while (stack.size > startStackSize) stack.pop() + while (stack.size > startStackSize) stack.pop() + } + + /** Explores the part of the ARG preserved from previous iterations (see lazy pruning). */ + private fun exploreLazily() { + while (stack.isNotEmpty()) { + val lazilyExplorable = + last.node.outEdges + .toList() + .filter { it.target.state.reExplored != true && it.action !in last.sleep } + .toSet() + if (lazilyExplorable.isEmpty()) return + + val edgeToExplore = lazilyExplorable.random(random) + val actionToExplore = edgeToExplore.action + last.backtrack.addAll(last.state.enabled.filter { it.pid == actionToExplore.pid }) + last.sleep.add(actionToExplore) + last.explored.add(actionToExplore) + add(edgeToExplore.target) } - - /** - * Explores the part of the ARG preserved from previous iterations (see lazy pruning). - */ - private fun exploreLazily() { - while (stack.isNotEmpty()) { - val lazilyExplorable = last.node.outEdges.toList() - .filter { it.target.state.reExplored != true && it.action !in last.sleep } - .toSet() - if (lazilyExplorable.isEmpty()) return - - val edgeToExplore = lazilyExplorable.random(random) - val actionToExplore = edgeToExplore.action - last.backtrack.addAll(last.state.enabled.filter { it.pid == actionToExplore.pid }) - last.sleep.add(actionToExplore) - last.explored.add(actionToExplore) - add(edgeToExplore.target) + } + + /** + * Returns a map with the keys of the original maps and the maximum of the reference numbers + * associated to each key. + */ + private fun max(map1: Map, map2: Map) = + (map1.keys union map2.keys) + .associateWith { key -> max(map1[key] ?: -1, map2[key] ?: -1) } + .toMutableMap() + + /** See the article for the definition of notdep. */ + private fun notdep(start: Int, action: A): List { + val e = stack[start].action + return stack + .slice(start + 1 until stack.size) + .filterIndexed { index, item -> + item.node.parent.get() == stack[start + 1 + index - 1].node && + !dependent(e, item.action) + } + .map { it.action } + .toMutableList() + .apply { add(action) } + } + + /** See the article for the definition of initials. */ + private fun initials(start: Int, sequence: List): Set { + val state = stack[start].node.state + return (state.enabled subtract (state.sleep subtract state.explored)) + .filter { enabledAction -> + for (action in sequence) { + if (action == enabledAction) { + return@filter true + } else if (dependent(enabledAction, action)) { + return@filter false + } } + false + } + .toSet() + } + + /** + * Returns true when the virtual exploration cannot detect any more races relevant in the + * "real" exploration (the part of the search stack before the first covering node). + */ + private fun noInfluenceOnRealExploration(realStackSize: Int) = + last.processLastAction.keys.all { process -> + last.lastDependents.containsKey(process) && + checkNotNull(last.lastDependents[process]).all { (_, index) -> index >= realStackSize } } - - /** - * Returns a map with the keys of the original maps and the maximum of the reference numbers associated to each key. - */ - private fun max(map1: Map, map2: Map) = - (map1.keys union map2.keys).associateWith { key -> - max( - map1[key] ?: -1, map2[key] ?: -1 - ) - }.toMutableMap() - - /** - * See the article for the definition of notdep. - */ - private fun notdep(start: Int, action: A): List { - val e = stack[start].action - return stack.slice(start + 1 until stack.size).filterIndexed { index, item -> - item.node.parent.get() == stack[start + 1 + index - 1].node && !dependent( - e, item.action - ) - }.map { it.action }.toMutableList().apply { add(action) } - } - - /** - * See the article for the definition of initials. - */ - private fun initials(start: Int, sequence: List): Set { - val state = stack[start].node.state - return (state.enabled subtract (state.sleep subtract state.explored)).filter { enabledAction -> - for (action in sequence) { - if (action == enabledAction) { - return@filter true - } else if (dependent(enabledAction, action)) { - return@filter false - } - } - false - }.toSet() - } - - /** - * Returns true when the virtual exploration cannot detect any more races relevant in the "real" exploration (the part of the search stack before the first covering node). - */ - private fun noInfluenceOnRealExploration(realStackSize: Int) = - last.processLastAction.keys.all { process -> - last.lastDependents.containsKey(process) && - checkNotNull(last.lastDependents[process]).all { (_, index) -> - index >= realStackSize - } - } } - /** - * Returns true if a and b are dependent actions. - */ - protected open fun dependent(a: A, b: A): Boolean { - if (a.pid == b.pid) return true + /** Returns true if a and b are dependent actions. */ + protected open fun dependent(a: A, b: A): Boolean { + if (a.pid == b.pid) return true - val aGlobalVars = a.edge.collectIndirectGlobalVarAccesses(xcfa) - val bGlobalVars = b.edge.collectIndirectGlobalVarAccesses(xcfa) - // dependent if they access the same variable (at least one write) - return (aGlobalVars.keys intersect bGlobalVars.keys).any { aGlobalVars[it].isWritten || bGlobalVars[it].isWritten } + val aGlobalVars = a.edge.collectIndirectGlobalVarAccesses(xcfa) + val bGlobalVars = b.edge.collectIndirectGlobalVarAccesses(xcfa) + // dependent if they access the same variable (at least one write) + return (aGlobalVars.keys intersect bGlobalVars.keys).any { + aGlobalVars[it].isWritten || bGlobalVars[it].isWritten } + } } /** @@ -469,29 +464,29 @@ open class XcfaDporLts(private val xcfa: XCFA) : LTS { */ class XcfaAadporLts(private val xcfa: XCFA) : XcfaDporLts(xcfa) { - /** - * The current precision of the abstraction. - */ - private var prec: Prec? = null - - /** - * Returns actions to be explored from the given state considering the given precision. - */ - override fun

getEnabledActionsFor(state: S, exploredActions: Collection, prec: P): Set { - this.prec = prec - return getEnabledActionsFor(state) - } - - /** - * Returns true if a and b are dependent actions in the current abstraction. - */ - override fun dependent(a: A, b: A): Boolean { - if (a.pid == b.pid) return true - - val precVars = prec?.usedVars?.toSet() ?: return super.dependent(a, b) - val aGlobalVars = a.edge.collectIndirectGlobalVarAccesses(xcfa) - val bGlobalVars = b.edge.collectIndirectGlobalVarAccesses(xcfa) - // dependent if they access the same variable in the precision (at least one write) - return (aGlobalVars.keys intersect bGlobalVars.keys intersect precVars).any { aGlobalVars[it].isWritten || bGlobalVars[it].isWritten } + /** The current precision of the abstraction. */ + private var prec: Prec? = null + + /** Returns actions to be explored from the given state considering the given precision. */ + override fun

getEnabledActionsFor( + state: S, + exploredActions: Collection, + prec: P, + ): Set { + this.prec = prec + return getEnabledActionsFor(state) + } + + /** Returns true if a and b are dependent actions in the current abstraction. */ + override fun dependent(a: A, b: A): Boolean { + if (a.pid == b.pid) return true + + val precVars = prec?.usedVars?.toSet() ?: return super.dependent(a, b) + val aGlobalVars = a.edge.collectIndirectGlobalVarAccesses(xcfa) + val bGlobalVars = b.edge.collectIndirectGlobalVarAccesses(xcfa) + // dependent if they access the same variable in the precision (at least one write) + return (aGlobalVars.keys intersect bGlobalVars.keys intersect precVars).any { + aGlobalVars[it].isWritten || bGlobalVars[it].isWritten } + } } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt index 5fe04637e1..f54208f3bc 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/ExecuteConfig.kt @@ -243,10 +243,12 @@ private fun backend( } .let { result -> when { - result.isSafe && - LoopUnrollPass.FORCE_UNROLL_USED -> { // cannot report safe if force unroll was used + result.isSafe && LoopUnrollPass.FORCE_UNROLL_USED -> { + // cannot report safe if force unroll was used logger.write(RESULT, "Incomplete loop unroll used: safe result is unreliable.\n") - SafetyResult.unknown() + if (config.outputConfig.acceptUnreliableSafe) + result // for comparison with BMC tools + else SafetyResult.unknown() } else -> result diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToChecker.kt index 4bd2dce149..2861be5cb9 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToChecker.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import hu.bme.mit.theta.analysis.Trace @@ -31,20 +30,32 @@ import hu.bme.mit.theta.xcfa.cli.params.Backend import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig import hu.bme.mit.theta.xcfa.model.XCFA -fun getChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, parseContext: ParseContext, - logger: Logger, - uniqueLogger: Logger): SafetyChecker<*, *, XcfaPrec<*>> = - if (config.backendConfig.inProcess) { - InProcessChecker(xcfa, config, parseContext, logger) - } else { - when (config.backendConfig.backend) { - Backend.CEGAR -> getCegarChecker(xcfa, mcm, config, logger) - Backend.BOUNDED -> getBoundedChecker(xcfa, mcm, config, logger) - Backend.OC -> getOcChecker(xcfa, mcm, config, logger) - Backend.LAZY -> TODO() - Backend.PORTFOLIO -> getPortfolioChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger) - Backend.NONE -> SafetyChecker>, XcfaAction>, Trace>, XcfaAction>, XcfaPrec<*>> { _ -> SafetyResult.unknown() } - Backend.CHC -> getHornChecker(xcfa, mcm, config, logger) +fun getChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + parseContext: ParseContext, + logger: Logger, + uniqueLogger: Logger, +): SafetyChecker<*, *, XcfaPrec<*>> = + if (config.backendConfig.inProcess) { + InProcessChecker(xcfa, config, parseContext, logger) + } else { + when (config.backendConfig.backend) { + Backend.CEGAR -> getCegarChecker(xcfa, mcm, config, logger) + Backend.BOUNDED -> getBoundedChecker(xcfa, mcm, config, logger) + Backend.OC -> getOcChecker(xcfa, mcm, config, logger) + Backend.LAZY -> TODO() + Backend.PORTFOLIO -> + getPortfolioChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger) + Backend.NONE -> + SafetyChecker< + ARG>, XcfaAction>, + Trace>, XcfaAction>, + XcfaPrec<*>, + > { _ -> + SafetyResult.unknown() } + Backend.CHC -> getHornChecker(xcfa, mcm, config, logger) } - + } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt index d8e3fc76ff..bef5ea5729 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt @@ -15,16 +15,12 @@ */ package hu.bme.mit.theta.xcfa.cli.checkers -import hu.bme.mit.theta.analysis.Trace +import hu.bme.mit.theta.analysis.Cex import hu.bme.mit.theta.analysis.algorithm.EmptyProof import hu.bme.mit.theta.analysis.algorithm.SafetyChecker -import hu.bme.mit.theta.analysis.algorithm.SafetyResult -import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM -import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaPrec -import hu.bme.mit.theta.xcfa.analysis.XcfaState import hu.bme.mit.theta.xcfa.analysis.oc.XcfaOcChecker import hu.bme.mit.theta.xcfa.cli.params.OcConfig import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig @@ -35,16 +31,17 @@ fun getOcChecker( mcm: MCM, config: XcfaConfig<*, *>, logger: Logger, -): SafetyChecker>, XcfaAction>, XcfaPrec<*>> { +): SafetyChecker> { + val ocConfig = config.backendConfig.specConfig as OcConfig val ocChecker = - XcfaOcChecker(xcfa, (config.backendConfig.specConfig as OcConfig).decisionProcedure, logger) - return object : - SafetyChecker>, XcfaAction>, XcfaPrec<*>> { - override fun check( - prec: XcfaPrec<*>? - ): SafetyResult>, XcfaAction>> = check() - - override fun check(): SafetyResult>, XcfaAction>> = - ocChecker.check() - } + XcfaOcChecker( + xcfa, + ocConfig.decisionProcedure, + logger, + ocConfig.inputConflictClauseFile, + ocConfig.outputConflictClauses, + ocConfig.nonPermissiveValidation, + ocConfig.autoConflict, + ) + return SafetyChecker { ocChecker.check() } } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToPortfolio.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToPortfolio.kt index a105f36786..845fe9e171 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToPortfolio.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToPortfolio.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.checkers import com.google.common.base.Stopwatch @@ -40,47 +39,64 @@ import javax.script.ScriptEngine import javax.script.ScriptEngineManager import javax.script.SimpleBindings -fun getPortfolioChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, - parseContext: ParseContext, logger: Logger, - uniqueLogger: Logger): SafetyChecker>, XcfaAction>, Trace>, XcfaAction>, XcfaPrec<*>> = SafetyChecker { _ -> - - val sw = Stopwatch.createStarted() - val portfolioName = (config.backendConfig.specConfig as PortfolioConfig).portfolio +fun getPortfolioChecker( + xcfa: XCFA, + mcm: MCM, + config: XcfaConfig<*, *>, + parseContext: ParseContext, + logger: Logger, + uniqueLogger: Logger, +): SafetyChecker< + ARG>, XcfaAction>, + Trace>, XcfaAction>, + XcfaPrec<*>, +> = SafetyChecker { _ -> + val sw = Stopwatch.createStarted() + val portfolioName = (config.backendConfig.specConfig as PortfolioConfig).portfolio - val portfolioStm = when (portfolioName) { - "STABLE", - "CEGAR", - "COMPLEX", - "COMPLEX24" -> complexPortfolio24(xcfa, mcm, parseContext, config, logger, uniqueLogger) + val portfolioStm = + when (portfolioName) { + "STABLE", + "CEGAR", + "COMPLEX", + "COMPLEX24" -> complexPortfolio24(xcfa, mcm, parseContext, config, logger, uniqueLogger) - "COMPLEX23" -> complexPortfolio23(xcfa, mcm, parseContext, config, logger, uniqueLogger) + "COMPLEX23" -> complexPortfolio23(xcfa, mcm, parseContext, config, logger, uniqueLogger) - "EMERGENT", - "BOUNDED" -> boundedPortfolio(xcfa, mcm, parseContext, config, logger, uniqueLogger) + "EMERGENT", + "BOUNDED" -> boundedPortfolio(xcfa, mcm, parseContext, config, logger, uniqueLogger) - "TESTING", - "CHC", - "HORN" -> hornPortfolio(xcfa, mcm, parseContext, config, logger, uniqueLogger) + "TESTING", + "CHC", + "HORN" -> hornPortfolio(xcfa, mcm, parseContext, config, logger, uniqueLogger) - else -> { - if (File(portfolioName).exists()) { - val kotlinEngine: ScriptEngine = ScriptEngineManager().getEngineByExtension("kts") - val bindings: Bindings = SimpleBindings() - bindings["xcfa"] = xcfa - bindings["mcm"] = mcm - bindings["parseContext"] = parseContext - bindings["portfolioConfig"] = config - bindings["logger"] = logger - bindings["uniqueLogger"] = uniqueLogger - kotlinEngine.eval(FileReader(portfolioName), bindings) as STM - } else { - error("No such file or built-in config: $portfolioName") - } + else -> { + if (File(portfolioName).exists()) { + val kotlinEngine: ScriptEngine = ScriptEngineManager().getEngineByExtension("kts") + val bindings: Bindings = SimpleBindings() + bindings["xcfa"] = xcfa + bindings["mcm"] = mcm + bindings["parseContext"] = parseContext + bindings["portfolioConfig"] = config + bindings["logger"] = logger + bindings["uniqueLogger"] = uniqueLogger + kotlinEngine.eval(FileReader(portfolioName), bindings) as STM + } else { + error("No such file or built-in config: $portfolioName") } + } } - val result = portfolioStm.execute() as Pair, SafetyResult<*, *>> + val result = portfolioStm.execute() as Pair, SafetyResult<*, *>> - logger.write(Logger.Level.RESULT, "Config ${result.first} succeeded in ${sw.elapsed(TimeUnit.MILLISECONDS)} ms\n") - result.second as SafetyResult>, XcfaAction>, Trace>, XcfaAction>>? -} \ No newline at end of file + logger.write( + Logger.Level.RESULT, + "Config ${result.first} succeeded in ${sw.elapsed(TimeUnit.MILLISECONDS)} ms\n", + ) + result.second + as + SafetyResult< + ARG>, XcfaAction>, + Trace>, XcfaAction>, + >? +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt index a52a05c957..ca5d3637c7 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/XcfaConfig.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.params import com.beust.jcommander.Parameter @@ -25,395 +24,447 @@ import hu.bme.mit.theta.frontend.transformation.ArchitectureConfig import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager import hu.bme.mit.theta.xcfa.analysis.ErrorDetection +import hu.bme.mit.theta.xcfa.analysis.oc.AutoConflictFinderConfig import hu.bme.mit.theta.xcfa.analysis.oc.OcDecisionProcedureType import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.passes.LbePass import java.io.File import java.nio.file.Paths - interface Config { - fun getObjects(): Set = setOf(this) - fun update(): Boolean = false + fun getObjects(): Set = setOf(this) + + fun update(): Boolean = false } interface SpecializableConfig : Config { - val specConfig: T? - override fun getObjects(): Set = setOf(this) union (specConfig?.getObjects() ?: setOf()) - fun createSpecConfig() - override fun update(): Boolean = specConfig?.update() ?: createSpecConfig().let { specConfig != null } + val specConfig: T? + + override fun getObjects(): Set = setOf(this) union (specConfig?.getObjects() ?: setOf()) + + fun createSpecConfig() + + override fun update(): Boolean = + specConfig?.update() ?: createSpecConfig().let { specConfig != null } } data class XcfaConfig( - val inputConfig: InputConfig = InputConfig(), - val frontendConfig: FrontendConfig = FrontendConfig(), - val backendConfig: BackendConfig = BackendConfig(), - val outputConfig: OutputConfig = OutputConfig(), - val debugConfig: DebugConfig = DebugConfig(), + val inputConfig: InputConfig = InputConfig(), + val frontendConfig: FrontendConfig = FrontendConfig(), + val backendConfig: BackendConfig = BackendConfig(), + val outputConfig: OutputConfig = OutputConfig(), + val debugConfig: DebugConfig = DebugConfig(), ) : Config { - override fun getObjects(): Set { - return inputConfig.getObjects() union frontendConfig.getObjects() union backendConfig.getObjects() union outputConfig.getObjects() union debugConfig.getObjects() - } - - override fun update(): Boolean = - listOf(inputConfig, frontendConfig, backendConfig, outputConfig, debugConfig).map { it.update() } - .any { it == true } + override fun getObjects(): Set { + return inputConfig.getObjects() union + frontendConfig.getObjects() union + backendConfig.getObjects() union + outputConfig.getObjects() union + debugConfig.getObjects() + } + + override fun update(): Boolean = + listOf(inputConfig, frontendConfig, backendConfig, outputConfig, debugConfig) + .map { it.update() } + .any { it == true } } data class InputConfig( - @Parameter(names = ["--input"], description = "Path of the input model") - var input: File? = null, - - @Parameter(names = ["--cat"], description = "Path of the cat model") - var catFile: File? = null, - - @Parameter(names = ["--parse-ctx"], - description = "Path of the parse context JSON (may contain additional metadata)") - var parseCtx: File? = null, - - @Parameter(names = ["--xcfa-w-ctx"], - description = "XCFA and ParseContext (will overwrite --input and --parse-ctx when given)") - var xcfaWCtx: Triple? = null, - - @Parameter(names = ["--property"], - description = "Path of the property file (will overwrite --property when given)") - var propertyFile: File? = null, - - @Parameter(names = ["--property-value"], description = "Property") - var property: ErrorDetection = ErrorDetection.ERROR_LOCATION + @Parameter(names = ["--input"], description = "Path of the input model") var input: File? = null, + @Parameter(names = ["--cat"], description = "Path of the cat model") var catFile: File? = null, + @Parameter( + names = ["--parse-ctx"], + description = "Path of the parse context JSON (may contain additional metadata)", + ) + var parseCtx: File? = null, + @Parameter( + names = ["--xcfa-w-ctx"], + description = "XCFA and ParseContext (will overwrite --input and --parse-ctx when given)", + ) + var xcfaWCtx: Triple? = null, + @Parameter( + names = ["--property"], + description = "Path of the property file (will overwrite --property when given)", + ) + var propertyFile: File? = null, + @Parameter(names = ["--property-value"], description = "Property") + var property: ErrorDetection = ErrorDetection.ERROR_LOCATION, ) : Config { - override fun toString(): String { - return "InputConfig(inputFile=${input}, catFile=${catFile}, parseCtx=${parseCtx}, " + - "xcfaWCtx=${xcfaWCtx?.let { "present" } ?: "missing"}, propertyFile=${propertyFile}, property=${property}" - } + override fun toString(): String { + return "InputConfig(inputFile=${input}, catFile=${catFile}, parseCtx=${parseCtx}, " + + "xcfaWCtx=${xcfaWCtx?.let { "present" } ?: "missing"}, propertyFile=${propertyFile}, property=${property}" + } } interface SpecFrontendConfig : Config -data class FrontendConfig( - @Parameter(names = ["--lbe"], description = "Level of LBE (NO_LBE, LBE_LOCAL, LBE_SEQ, LBE_FULL)") - var lbeLevel: LbePass.LbeLevel = LbePass.LbeLevel.LBE_SEQ, - - @Parameter(names = ["--static-coi"], description = "Enable static cone-of-influence") - var staticCoi: Boolean = false, - - @Parameter(names = ["--unroll"], - description = "Max number of loop iterations to unroll (use -1 to unroll completely when possible)") - var loopUnroll: Int = 1000, - - @Parameter(names = ["--force-unroll"], - description = "Number of loop iteration to unroll even if the number of iterations is unknown; in case of such a bounded loop unrolling, the safety result cannot be safe (use -1 to disable)") - var forceUnroll: Int = -1, - - @Parameter(names = ["--enable-few"], - description = "Enable the FetchExecuteWriteback pass, which introduces a local temp var for all memory accesses") - var enableFew: Boolean = false, - @Parameter(names = ["--input-type"], description = "Format of the input") - var inputType: InputType = InputType.C, - - override var specConfig: T? = null +data class FrontendConfig( + @Parameter(names = ["--lbe"], description = "Level of LBE (NO_LBE, LBE_LOCAL, LBE_SEQ, LBE_FULL)") + var lbeLevel: LbePass.LbeLevel = LbePass.LbeLevel.LBE_SEQ, + @Parameter(names = ["--static-coi"], description = "Enable static cone-of-influence") + var staticCoi: Boolean = false, + @Parameter( + names = ["--unroll"], + description = + "Max number of loop iterations to unroll (use -1 to unroll completely when possible)", + ) + var loopUnroll: Int = 1000, + @Parameter( + names = ["--force-unroll"], + description = + "Number of loop iteration to unroll even if the number of iterations is unknown; in case of such a bounded loop unrolling, the safety result cannot be safe (use -1 to disable)", + ) + var forceUnroll: Int = -1, + @Parameter( + names = ["--enable-few"], + description = + "Enable the FetchExecuteWriteback pass, which introduces a local temp var for all memory accesses", + ) + var enableFew: Boolean = false, + @Parameter(names = ["--input-type"], description = "Format of the input") + var inputType: InputType = InputType.C, + override var specConfig: T? = null, ) : SpecializableConfig { - override fun createSpecConfig() { - specConfig = when (inputType) { - InputType.C -> CFrontendConfig() as T - InputType.LLVM -> null - InputType.JSON -> null - InputType.DSL -> null - InputType.LITMUS -> null - InputType.CHC -> CHCFrontendConfig() as T - } - } + override fun createSpecConfig() { + specConfig = + when (inputType) { + InputType.C -> CFrontendConfig() as T + InputType.LLVM -> null + InputType.JSON -> null + InputType.DSL -> null + InputType.LITMUS -> null + InputType.CHC -> CHCFrontendConfig() as T + } + } } data class CFrontendConfig( - @Parameter(names = ["--arithmetic"], description = "Arithmetic type (efficient, bitvector, integer)") - var arithmetic: ArchitectureConfig.ArithmeticType = ArchitectureConfig.ArithmeticType.efficient, - - @Parameter(names = ["--architecture"], description = "Architecture (see https://unix.org/whitepapers/64bit.html)") - var architecture: ArchitectureConfig.ArchitectureType = ArchitectureConfig.ArchitectureType.LP64, + @Parameter( + names = ["--arithmetic"], + description = "Arithmetic type (efficient, bitvector, integer)", + ) + var arithmetic: ArchitectureConfig.ArithmeticType = ArchitectureConfig.ArithmeticType.efficient, + @Parameter( + names = ["--architecture"], + description = "Architecture (see https://unix.org/whitepapers/64bit.html)", + ) + var architecture: ArchitectureConfig.ArchitectureType = ArchitectureConfig.ArchitectureType.LP64, ) : SpecFrontendConfig data class CHCFrontendConfig( - @Parameter(names = ["--chc-transformation"], description = "Direction of transformation from CHC to XCFA") - var chcTransformation: ChcFrontend.ChcTransformation = ChcFrontend.ChcTransformation.PORTFOLIO, + @Parameter( + names = ["--chc-transformation"], + description = "Direction of transformation from CHC to XCFA", + ) + var chcTransformation: ChcFrontend.ChcTransformation = ChcFrontend.ChcTransformation.PORTFOLIO ) : SpecFrontendConfig interface SpecBackendConfig : Config data class BackendConfig( - @Parameter(names = ["--backend"], description = "Backend analysis to use") - var backend: Backend = Backend.CEGAR, - - @Parameter(names = ["--smt-home"], description = "The path of the solver registry") - var solverHome: String = SmtLibSolverManager.HOME.toAbsolutePath().toString(), - - @Parameter(names = ["--timeout-ms"], description = "Timeout for verification, use 0 for no timeout") - var timeoutMs: Long = 0, - - @Parameter(names = ["--in-process"], description = "Run analysis in process") - var inProcess: Boolean = false, - - override var specConfig: T? = null + @Parameter(names = ["--backend"], description = "Backend analysis to use") + var backend: Backend = Backend.CEGAR, + @Parameter(names = ["--smt-home"], description = "The path of the solver registry") + var solverHome: String = SmtLibSolverManager.HOME.toAbsolutePath().toString(), + @Parameter( + names = ["--timeout-ms"], + description = "Timeout for verification, use 0 for no timeout", + ) + var timeoutMs: Long = 0, + @Parameter(names = ["--in-process"], description = "Run analysis in process") + var inProcess: Boolean = false, + override var specConfig: T? = null, ) : SpecializableConfig { - override fun createSpecConfig() { - specConfig = when (backend) { - Backend.CEGAR -> CegarConfig() as T - Backend.BOUNDED -> BoundedConfig() as T - Backend.CHC -> HornConfig() as T - Backend.OC -> OcConfig() as T - Backend.LAZY -> null - Backend.PORTFOLIO -> PortfolioConfig() as T - Backend.NONE -> null - } - } + override fun createSpecConfig() { + specConfig = + when (backend) { + Backend.CEGAR -> CegarConfig() as T + Backend.BOUNDED -> BoundedConfig() as T + Backend.CHC -> HornConfig() as T + Backend.OC -> OcConfig() as T + Backend.LAZY -> null + Backend.PORTFOLIO -> PortfolioConfig() as T + Backend.NONE -> null + } + } } data class CegarConfig( - @Parameter(names = ["--initprec"], description = "Initial precision") - var initPrec: InitPrec = InitPrec.EMPTY, - - @Parameter(names = ["--por-level"], description = "POR dependency level") - var porLevel: POR = POR.NOPOR, - - @Parameter(names = ["--por-seed"], description = "Random seed used for DPOR") - var porRandomSeed: Int = -1, - - @Parameter(names = ["--coi"], description = "Enable ConeOfInfluence") - var coi: ConeOfInfluenceMode = ConeOfInfluenceMode.NO_COI, - - @Parameter(names = ["--cex-monitor"], description = "Option to enable(CHECK)/disable(DISABLE) the CexMonitor") - var cexMonitor: CexMonitorOptions = CexMonitorOptions.CHECK, - - val abstractorConfig: CegarAbstractorConfig = CegarAbstractorConfig(), - val refinerConfig: CegarRefinerConfig = CegarRefinerConfig() + @Parameter(names = ["--initprec"], description = "Initial precision") + var initPrec: InitPrec = InitPrec.EMPTY, + @Parameter(names = ["--por-level"], description = "POR dependency level") + var porLevel: POR = POR.NOPOR, + @Parameter(names = ["--por-seed"], description = "Random seed used for DPOR") + var porRandomSeed: Int = -1, + @Parameter(names = ["--coi"], description = "Enable ConeOfInfluence") + var coi: ConeOfInfluenceMode = ConeOfInfluenceMode.NO_COI, + @Parameter( + names = ["--cex-monitor"], + description = "Option to enable(CHECK)/disable(DISABLE) the CexMonitor", + ) + var cexMonitor: CexMonitorOptions = CexMonitorOptions.CHECK, + val abstractorConfig: CegarAbstractorConfig = CegarAbstractorConfig(), + val refinerConfig: CegarRefinerConfig = CegarRefinerConfig(), ) : SpecBackendConfig { - override fun getObjects(): Set { - return super.getObjects() union abstractorConfig.getObjects() union refinerConfig.getObjects() - } + override fun getObjects(): Set { + return super.getObjects() union abstractorConfig.getObjects() union refinerConfig.getObjects() + } - override fun update(): Boolean = - listOf(abstractorConfig, refinerConfig).map { it.update() }.any { it } + override fun update(): Boolean = + listOf(abstractorConfig, refinerConfig).map { it.update() }.any { it } } data class CegarAbstractorConfig( - @Parameter(names = ["--abstraction-solver"], description = "Abstraction solver name") - var abstractionSolver: String = "Z3", - - @Parameter(names = ["--validate-abstraction-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateAbstractionSolver: Boolean = false, - - @Parameter(names = ["--domain"], description = "Abstraction domain") - var domain: Domain = Domain.EXPL, - - @Parameter(names = ["--maxenum"], - description = "How many successors to enumerate in a transition. Only relevant to the explicit domain. Use 0 for no limit.") - var maxEnum: Int = 1, - - @Parameter(names = ["--search"], description = "Search strategy") - var search: Search = Search.ERR, - - @Parameter(names = ["--havoc-memory"], - description = "HAVOC memory model (do not track pointers in transition function)") - var havocMemory: Boolean = false + @Parameter(names = ["--abstraction-solver"], description = "Abstraction solver name") + var abstractionSolver: String = "Z3", + @Parameter( + names = ["--validate-abstraction-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateAbstractionSolver: Boolean = false, + @Parameter(names = ["--domain"], description = "Abstraction domain") + var domain: Domain = Domain.EXPL, + @Parameter( + names = ["--maxenum"], + description = + "How many successors to enumerate in a transition. Only relevant to the explicit domain. Use 0 for no limit.", + ) + var maxEnum: Int = 1, + @Parameter(names = ["--search"], description = "Search strategy") var search: Search = Search.ERR, + @Parameter( + names = ["--havoc-memory"], + description = "HAVOC memory model (do not track pointers in transition function)", + ) + var havocMemory: Boolean = false, ) : Config data class CegarRefinerConfig( - @Parameter(names = ["--refinement-solver"], description = "Refinement solver name") - var refinementSolver: String = "Z3", - - @Parameter(names = ["--validate-refinement-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateRefinementSolver: Boolean = false, - - @Parameter(names = ["--refinement"], description = "Refinement strategy") - var refinement: Refinement = Refinement.SEQ_ITP, - - @Parameter(names = ["--predsplit"], description = "Predicate splitting (for predicate abstraction)") - var exprSplitter: ExprSplitterOptions = ExprSplitterOptions.WHOLE, - - @Parameter(names = ["--prunestrategy"], description = "Strategy for pruning the ARG after refinement") - var pruneStrategy: PruneStrategy = PruneStrategy.LAZY, + @Parameter(names = ["--refinement-solver"], description = "Refinement solver name") + var refinementSolver: String = "Z3", + @Parameter( + names = ["--validate-refinement-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateRefinementSolver: Boolean = false, + @Parameter(names = ["--refinement"], description = "Refinement strategy") + var refinement: Refinement = Refinement.SEQ_ITP, + @Parameter( + names = ["--predsplit"], + description = "Predicate splitting (for predicate abstraction)", + ) + var exprSplitter: ExprSplitterOptions = ExprSplitterOptions.WHOLE, + @Parameter( + names = ["--prunestrategy"], + description = "Strategy for pruning the ARG after refinement", + ) + var pruneStrategy: PruneStrategy = PruneStrategy.LAZY, ) : Config data class HornConfig( - @Parameter(names = ["--solver"], description = "Solver to use.") - var solver: String = "Z3:4.13", - @Parameter( - names = ["--validate-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues." - ) - var validateSolver: Boolean = false, + @Parameter(names = ["--solver"], description = "Solver to use.") var solver: String = "Z3:4.13", + @Parameter( + names = ["--validate-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateSolver: Boolean = false, ) : SpecBackendConfig data class BoundedConfig( - @Parameter(names = ["--max-bound"], description = "Maximum bound to check. Use 0 for no limit.") - var maxBound: Int = 0, - - val bmcConfig: BMCConfig = BMCConfig(), - val indConfig: InductionConfig = InductionConfig(), - val itpConfig: InterpolationConfig = InterpolationConfig(), + @Parameter(names = ["--max-bound"], description = "Maximum bound to check. Use 0 for no limit.") + var maxBound: Int = 0, + val bmcConfig: BMCConfig = BMCConfig(), + val indConfig: InductionConfig = InductionConfig(), + val itpConfig: InterpolationConfig = InterpolationConfig(), ) : SpecBackendConfig { - override fun getObjects(): Set { - return super.getObjects() union bmcConfig.getObjects() union indConfig.getObjects() union itpConfig.getObjects() - } - - override fun update(): Boolean = - listOf(bmcConfig, indConfig, itpConfig).map { it.update() }.any { it } + override fun getObjects(): Set { + return super.getObjects() union + bmcConfig.getObjects() union + indConfig.getObjects() union + itpConfig.getObjects() + } + override fun update(): Boolean = + listOf(bmcConfig, indConfig, itpConfig).map { it.update() }.any { it } } data class BMCConfig( - @Parameter(names = ["--no-bmc"], description = "Disable SAT check") - var disable: Boolean = false, - - @Parameter(names = ["--non-lf-path"], description = "Disable loop-freeness check") - var nonLfPath: Boolean = false, - - @Parameter(names = ["--bmc-solver"], description = "BMC solver name") - var bmcSolver: String = "Z3", - - @Parameter(names = ["--validate-bmc-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateBMCSolver: Boolean = false, + @Parameter(names = ["--no-bmc"], description = "Disable SAT check") var disable: Boolean = false, + @Parameter(names = ["--non-lf-path"], description = "Disable loop-freeness check") + var nonLfPath: Boolean = false, + @Parameter(names = ["--bmc-solver"], description = "BMC solver name") + var bmcSolver: String = "Z3", + @Parameter( + names = ["--validate-bmc-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateBMCSolver: Boolean = false, ) : Config data class InductionConfig( - @Parameter(names = ["--no-induction"], description = "Disable induction check") - var disable: Boolean = false, - - @Parameter(names = ["--induction-solver", "--ind-solver"], description = "Induction solver name") - var indSolver: String = "Z3", - - @Parameter(names = ["--validate-induction-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateIndSolver: Boolean = false, - - @Parameter(names = ["--ind-min-bound"], - description = "Start induction after reaching this bound") - var indMinBound: Int = 0, - - @Parameter(names = ["--ind-frequency"], - description = "Frequency of induction check") - var indFreq: Int = 1, + @Parameter(names = ["--no-induction"], description = "Disable induction check") + var disable: Boolean = false, + @Parameter(names = ["--induction-solver", "--ind-solver"], description = "Induction solver name") + var indSolver: String = "Z3", + @Parameter( + names = ["--validate-induction-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateIndSolver: Boolean = false, + @Parameter(names = ["--ind-min-bound"], description = "Start induction after reaching this bound") + var indMinBound: Int = 0, + @Parameter(names = ["--ind-frequency"], description = "Frequency of induction check") + var indFreq: Int = 1, ) : Config data class InterpolationConfig( - @Parameter(names = ["--no-interpolation"], description = "Disable interpolation check") - var disable: Boolean = false, - - @Parameter(names = ["--interpolation-solver", "--itp-solver"], description = "Interpolation solver name") - var itpSolver: String = "Z3", - - @Parameter(names = ["--validate-interpolation-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateItpSolver: Boolean = false, - - ) : Config + @Parameter(names = ["--no-interpolation"], description = "Disable interpolation check") + var disable: Boolean = false, + @Parameter( + names = ["--interpolation-solver", "--itp-solver"], + description = "Interpolation solver name", + ) + var itpSolver: String = "Z3", + @Parameter( + names = ["--validate-interpolation-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateItpSolver: Boolean = false, +) : Config data class OcConfig( - @Parameter(names = ["--oc-decision-procedure"], description = "Decision procedure for ordering-consistency check") - var decisionProcedure: OcDecisionProcedureType = OcDecisionProcedureType.PROPAGATOR, + @Parameter( + names = ["--oc-decision-procedure"], + description = "Decision procedure for ordering-consistency check", + ) + var decisionProcedure: OcDecisionProcedureType = OcDecisionProcedureType.PROPAGATOR, + @Parameter(names = ["--input-conflicts"], description = "Input file containing conflict clauses") + var inputConflictClauseFile: String? = null, + @Parameter(names = ["--output-conflicts"], description = "Enables conflict clause logging") + var outputConflictClauses: Boolean = false, + @Parameter( + names = ["--input-conflict-decision-procedure"], + description = "Output file to write conflict clauses", + ) + var inputConflictDecisionProcedure: String = "", + @Parameter( + names = ["--non-permissive-validation"], + description = "Output file to write conflict clauses", + ) + var nonPermissiveValidation: Boolean = false, + @Parameter( + names = ["--auto-conflict"], + description = "Level of manual conflict detection before verification", + ) + var autoConflict: AutoConflictFinderConfig = AutoConflictFinderConfig.NONE, ) : SpecBackendConfig data class PortfolioConfig( - @Parameter(names = ["--portfolio"], description = "Portfolio to run") - var portfolio: String = "COMPLEX", + @Parameter(names = ["--portfolio"], description = "Portfolio to run") + var portfolio: String = "COMPLEX" ) : SpecBackendConfig data class OutputConfig( - @Parameter(names = ["--version"], description = "Display version", help = true) - var versionInfo: Boolean = false, - - @Parameter(names = ["--enable-output"], description = "Enable output files") - var enableOutput: Boolean = false, - - @Parameter(names = ["--output-directory"], description = "Specify the directory where the result files are stored") - var resultFolder: File = Paths.get("./").toFile(), - - val cOutputConfig: COutputConfig = COutputConfig(), - val xcfaOutputConfig: XcfaOutputConfig = XcfaOutputConfig(), - val chcOutputConfig: ChcOutputConfig = ChcOutputConfig(), - val witnessConfig: WitnessConfig = WitnessConfig(), - val argConfig: ArgConfig = ArgConfig(), + @Parameter(names = ["--version"], description = "Display version", help = true) + var versionInfo: Boolean = false, + @Parameter(names = ["--enable-output"], description = "Enable output files") + var enableOutput: Boolean = false, + @Parameter( + names = ["--output-directory"], + description = "Specify the directory where the result files are stored", + ) + var resultFolder: File = Paths.get("./").toFile(), + @Parameter( + names = ["--accept-unreliable-safe"], + description = "Accept safe results even with unsafe loop unroll", + ) + var acceptUnreliableSafe: Boolean = false, + val cOutputConfig: COutputConfig = COutputConfig(), + val xcfaOutputConfig: XcfaOutputConfig = XcfaOutputConfig(), + val chcOutputConfig: ChcOutputConfig = ChcOutputConfig(), + val witnessConfig: WitnessConfig = WitnessConfig(), + val argConfig: ArgConfig = ArgConfig(), ) : Config { - override fun getObjects(): Set { - return super.getObjects() union cOutputConfig.getObjects() union xcfaOutputConfig.getObjects() union chcOutputConfig.getObjects() union witnessConfig.getObjects() union argConfig.getObjects() - } - - override fun update(): Boolean = - listOf(cOutputConfig, xcfaOutputConfig, chcOutputConfig, witnessConfig, argConfig).map { it.update() } - .any { it } + override fun getObjects(): Set { + return super.getObjects() union + cOutputConfig.getObjects() union + xcfaOutputConfig.getObjects() union + chcOutputConfig.getObjects() union + witnessConfig.getObjects() union + argConfig.getObjects() + } + + override fun update(): Boolean = + listOf(cOutputConfig, xcfaOutputConfig, chcOutputConfig, witnessConfig, argConfig) + .map { it.update() } + .any { it } } data class XcfaOutputConfig( - @Parameter(names = ["--disable-xcfa-serialization"]) - var disable: Boolean = false, + @Parameter(names = ["--disable-xcfa-serialization"]) var disable: Boolean = false ) : Config data class ChcOutputConfig( - @Parameter(names = ["--disable-chc-serialization"]) - var disable: Boolean = false, + @Parameter(names = ["--disable-chc-serialization"]) var disable: Boolean = false ) : Config data class COutputConfig( - @Parameter(names = ["--disable-c-serialization"]) - var disable: Boolean = false, - - @Parameter(names = ["--to-c-use-arrays"]) - var useArr: Boolean = false, - - @Parameter(names = ["--to-c-use-exact-arrays"]) - var useExArr: Boolean = false, - - @Parameter(names = ["--to-c-use-ranges"]) - var useRange: Boolean = false + @Parameter(names = ["--disable-c-serialization"]) var disable: Boolean = false, + @Parameter(names = ["--to-c-use-arrays"]) var useArr: Boolean = false, + @Parameter(names = ["--to-c-use-exact-arrays"]) var useExArr: Boolean = false, + @Parameter(names = ["--to-c-use-ranges"]) var useRange: Boolean = false, ) : Config data class WitnessConfig( - @Parameter(names = ["--disable-witness-generation"]) - var disable: Boolean = false, - - @Parameter(names = ["--only-svcomp-witness"]) - var svcomp: Boolean = false, - - @Parameter(names = ["--cex-solver"], description = "Concretizer solver name") - var concretizerSolver: String = "Z3", - - @Parameter(names = ["--validate-cex-solver"], - description = "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.") - var validateConcretizerSolver: Boolean = false + @Parameter(names = ["--disable-witness-generation"]) var disable: Boolean = false, + @Parameter(names = ["--only-svcomp-witness"]) var svcomp: Boolean = false, + @Parameter(names = ["--cex-solver"], description = "Concretizer solver name") + var concretizerSolver: String = "Z3", + @Parameter( + names = ["--validate-cex-solver"], + description = + "Activates a wrapper, which validates the assertions in the solver in each (SAT) check. Filters some solver issues.", + ) + var validateConcretizerSolver: Boolean = false, ) : Config data class ArgConfig( - @Parameter(names = ["--disable-arg-generation"]) - var disable: Boolean = false, + @Parameter(names = ["--disable-arg-generation"]) var disable: Boolean = false ) : Config data class DebugConfig( - @Parameter(names = ["--debug"], description = "Debug mode (not exiting when encountering an exception)") - var debug: Boolean = false, - - @Parameter(names = ["--stacktrace"], description = "Print full stack trace in case of exception") - var stacktrace: Boolean = false, - - @Parameter(names = ["--loglevel"], description = "Detailedness of logging") - var logLevel: Logger.Level = Logger.Level.MAINSTEP, - - @Parameter(names = ["--arg-debug"], - description = "ARG debug mode (use the web-based debugger for ARG visualization)") - var argdebug: Boolean = false, - - @Parameter(names = ["--arg-to-file"], - description = "Visualize the resulting file here: https://ftsrg-edu.github.io/student-sisak-argviz/") - var argToFile: Boolean = false -) : Config \ No newline at end of file + @Parameter( + names = ["--debug"], + description = "Debug mode (not exiting when encountering an exception)", + ) + var debug: Boolean = false, + @Parameter(names = ["--stacktrace"], description = "Print full stack trace in case of exception") + var stacktrace: Boolean = false, + @Parameter(names = ["--loglevel"], description = "Detailedness of logging") + var logLevel: Logger.Level = Logger.Level.MAINSTEP, + @Parameter( + names = ["--arg-debug"], + description = "ARG debug mode (use the web-based debugger for ARG visualization)", + ) + var argdebug: Boolean = false, + @Parameter( + names = ["--arg-to-file"], + description = + "Visualize the resulting file here: https://ftsrg-edu.github.io/student-sisak-argviz/", + ) + var argToFile: Boolean = false, +) : Config diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/bounded.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/bounded.kt index 787774d93f..c96b399f38 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/bounded.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/bounded.kt @@ -13,17 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.portfolio -import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.frontend.transformation.ArchitectureConfig -import hu.bme.mit.theta.frontend.transformation.grammar.preprocess.ArithmeticTrait import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM -import hu.bme.mit.theta.solver.Solver -import hu.bme.mit.theta.xcfa.analysis.ErrorDetection import hu.bme.mit.theta.xcfa.analysis.isInlined import hu.bme.mit.theta.xcfa.cli.params.* import hu.bme.mit.theta.xcfa.cli.runConfig @@ -33,167 +28,223 @@ import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass import java.nio.file.Paths fun boundedPortfolio( - xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger): STM { - - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } - - var baseConfig = XcfaConfig( - inputConfig = InputConfig( - input = null, - xcfaWCtx = Triple(xcfa, mcm, parseContext), - propertyFile = null, - property = portfolioConfig.inputConfig.property), - frontendConfig = FrontendConfig( - lbeLevel = LbePass.level, - loopUnroll = LoopUnrollPass.UNROLL_LIMIT, - inputType = InputType.C, - specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient)), - backendConfig = BackendConfig( - backend = Backend.BOUNDED, - solverHome = portfolioConfig.backendConfig.solverHome, - timeoutMs = 0, - specConfig = BoundedConfig( - bmcConfig = BMCConfig(true), - maxBound = 0, - indConfig = InductionConfig(true), - itpConfig = InterpolationConfig(true) - )), - outputConfig = OutputConfig( - versionInfo = false, - resultFolder = Paths.get("./").toFile(), // cwd - cOutputConfig = COutputConfig(disable = true), - witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true), - enableOutput = portfolioConfig.outputConfig.enableOutput, + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): STM { + + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } + + var baseConfig = + XcfaConfig( + inputConfig = + InputConfig( + input = null, + xcfaWCtx = Triple(xcfa, mcm, parseContext), + propertyFile = null, + property = portfolioConfig.inputConfig.property, + ), + frontendConfig = + FrontendConfig( + lbeLevel = LbePass.level, + loopUnroll = LoopUnrollPass.UNROLL_LIMIT, + inputType = InputType.C, + specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient), + ), + backendConfig = + BackendConfig( + backend = Backend.BOUNDED, + solverHome = portfolioConfig.backendConfig.solverHome, + timeoutMs = 0, + specConfig = + BoundedConfig( + bmcConfig = BMCConfig(true), + maxBound = 0, + indConfig = InductionConfig(true), + itpConfig = InterpolationConfig(true), + ), ), - debugConfig = portfolioConfig.debugConfig + outputConfig = + OutputConfig( + versionInfo = false, + resultFolder = Paths.get("./").toFile(), // cwd + cOutputConfig = COutputConfig(disable = true), + witnessConfig = + WitnessConfig( + disable = false, + concretizerSolver = "Z3", + validateConcretizerSolver = false, + ), + argConfig = ArgConfig(disable = true), + enableOutput = portfolioConfig.outputConfig.enableOutput, + ), + debugConfig = portfolioConfig.debugConfig, ) - if (parseContext.multiThreading) { - throw UnsupportedOperationException("Multithreading for bounded checkers not supported") - } + if (parseContext.multiThreading) { + throw UnsupportedOperationException("Multithreading for bounded checkers not supported") + } - if (!xcfa.isInlined) { - throw UnsupportedOperationException("Recursive XCFA for bounded checkers not supported") - } + if (!xcfa.isInlined) { + throw UnsupportedOperationException("Recursive XCFA for bounded checkers not supported") + } - val timeoutOrNotSolvableError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - ErrorCodeException(ExitCodes.SERVER_ERROR.code), + val timeoutOrNotSolvableError = + ExceptionTrigger( + fallthroughExceptions = + setOf( + ErrorCodeException(ExitCodes.SOLVER_ERROR.code), + ErrorCodeException(ExitCodes.SERVER_ERROR.code), ), - label = "TimeoutOrNotSolvableError" + label = "TimeoutOrNotSolvableError", + ) + + val timeoutOrSolverError = + ExceptionTrigger( + fallthroughExceptions = setOf(ErrorCodeException(ExitCodes.SERVER_ERROR.code)), + label = "TimeoutOrSolverError", ) - val timeoutOrSolverError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SERVER_ERROR.code), + val solverError = + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") + + val anyError = ExceptionTrigger(label = "Anything") + + fun XcfaConfig<*, BoundedConfig>.adaptConfig( + bmcEnabled: Boolean = false, + indEnabled: Boolean = false, + itpEnabled: Boolean = false, + bmcSolver: String = "Z3", + indSolver: String = "Z3", + itpSolver: String = "cvc5:1.0.8", + timeoutMs: Long = 0, + inProcess: Boolean = this.backendConfig.inProcess, + ): XcfaConfig<*, BoundedConfig> { + return copy( + backendConfig = + backendConfig.copy( + timeoutMs = timeoutMs, + inProcess = inProcess, + specConfig = + backendConfig.specConfig!!.copy( + bmcConfig = + backendConfig.specConfig!! + .bmcConfig + .copy(disable = !bmcEnabled, bmcSolver = bmcSolver), + indConfig = + backendConfig.specConfig!! + .indConfig + .copy(disable = !indEnabled, indSolver = indSolver), + itpConfig = + backendConfig.specConfig!! + .itpConfig + .copy(disable = !itpEnabled, itpSolver = itpSolver), + ), + ) + ) + } + + fun getStm(inProcess: Boolean): STM { + val edges = LinkedHashSet() + val configBmcZ3 = + ConfigNode( + "BmcZ3-$inProcess", + baseConfig.adaptConfig(inProcess = inProcess, bmcEnabled = true, timeoutMs = 30000), + checker, + ) + val configBmcMathsat = + ConfigNode( + "BmcMathsat-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + bmcSolver = "mathsat:5.6.10", + bmcEnabled = true, + timeoutMs = 30000, + ), + checker, + ) + val configIndZ3 = + ConfigNode( + "IndZ3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + bmcEnabled = true, + indEnabled = true, + timeoutMs = 300000, + ), + checker, + ) + val configIndMathsat = + ConfigNode( + "IndMathsat-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + bmcSolver = "mathsat:5.6.10", + indSolver = "mathsat:5.6.10", + bmcEnabled = true, + indEnabled = true, + timeoutMs = 300000, + ), + checker, + ) + val configItpCvc5 = + ConfigNode( + "ItpCvc5-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + itpEnabled = true, + itpSolver = "cvc5:1.0.8", + timeoutMs = 0, ), - label = "TimeoutOrSolverError" + checker, + ) + val configItpMathsat = + ConfigNode( + "ItpMathsat-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + itpSolver = "mathsat:5.6.10", + itpEnabled = true, + timeoutMs = 0, + ), + checker, + ) + + edges.add(Edge(configBmcZ3, configBmcMathsat, solverError)) + edges.add( + Edge(configBmcZ3, configIndZ3, if (inProcess) timeoutOrNotSolvableError else anyError) + ) + edges.add( + Edge( + configBmcMathsat, + configIndMathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) ) - val solverError = ExceptionTrigger( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError" + edges.add(Edge(configIndZ3, configIndMathsat, solverError)) + edges.add( + Edge(configIndZ3, configItpCvc5, if (inProcess) timeoutOrNotSolvableError else anyError) ) + edges.add( + Edge(configIndMathsat, configItpCvc5, if (inProcess) timeoutOrNotSolvableError else anyError) + ) + + edges.add(Edge(configItpCvc5, configItpMathsat, anyError)) + + return STM(configBmcZ3, edges) + } + + logger.write(Logger.Level.RESULT, "Using bounded portfolio\n") + + val inProcess = HierarchicalNode("InProcess", getStm(true)) + val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) + + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - val anyError = ExceptionTrigger(label = "Anything") - - fun XcfaConfig<*, BoundedConfig>.adaptConfig( - bmcEnabled: Boolean = false, - indEnabled: Boolean = false, - itpEnabled: Boolean = false, - bmcSolver: String = "Z3", - indSolver: String = "Z3", - itpSolver: String = "cvc5:1.0.8", - timeoutMs: Long = 0, - inProcess: Boolean = this.backendConfig.inProcess - ): XcfaConfig<*, BoundedConfig> { - return copy(backendConfig = backendConfig.copy( - timeoutMs = timeoutMs, - inProcess = inProcess, - specConfig = backendConfig.specConfig!!.copy( - bmcConfig = backendConfig.specConfig!!.bmcConfig.copy(disable = !bmcEnabled, bmcSolver = bmcSolver), - indConfig = backendConfig.specConfig!!.indConfig.copy(disable = !indEnabled, indSolver = indSolver), - itpConfig = backendConfig.specConfig!!.itpConfig.copy(disable = !itpEnabled, itpSolver = itpSolver), - ) - )) - } - - fun getStm(inProcess: Boolean): STM { - val edges = LinkedHashSet() - val configBmcZ3 = ConfigNode("BmcZ3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - bmcEnabled = true, - timeoutMs = 30000 - ), checker) - val configBmcMathsat = ConfigNode("BmcMathsat-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - bmcSolver = "mathsat:5.6.10", - bmcEnabled = true, - timeoutMs = 30000 - ), checker) - val configIndZ3 = ConfigNode("IndZ3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - bmcEnabled = true, - indEnabled = true, - timeoutMs = 300000 - ), checker) - val configIndMathsat = ConfigNode("IndMathsat-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - bmcSolver = "mathsat:5.6.10", - indSolver = "mathsat:5.6.10", - bmcEnabled = true, - indEnabled = true, - timeoutMs = 300000 - ), checker) - val configItpCvc5 = ConfigNode("ItpCvc5-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - itpEnabled = true, - itpSolver = "cvc5:1.0.8", - timeoutMs = 0 - ), checker) - val configItpMathsat = ConfigNode("ItpMathsat-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - itpSolver = "mathsat:5.6.10", - itpEnabled = true, - timeoutMs = 0 - ), checker) - - edges.add(Edge(configBmcZ3, configBmcMathsat, solverError)) - edges.add(Edge(configBmcZ3, configIndZ3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(configBmcMathsat, configIndMathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - - edges.add(Edge(configIndZ3, configIndMathsat, solverError)) - edges.add(Edge(configIndZ3, configItpCvc5, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(configIndMathsat, configItpCvc5, - if (inProcess) timeoutOrNotSolvableError else anyError)) - - edges.add(Edge(configItpCvc5, configItpMathsat, anyError)) - - return STM(configBmcZ3, edges) - } - - logger.write(Logger.Level.RESULT, "Using bounded portfolio\n") - - val inProcess = HierarchicalNode("InProcess", getStm(true)) - val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) - - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - - return if (portfolioConfig.debugConfig.debug) getStm(false) else STM(inProcess, setOf(fallbackEdge)) -} \ No newline at end of file + return if (portfolioConfig.debugConfig.debug) getStm(false) + else STM(inProcess, setOf(fallbackEdge)) +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex23.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex23.kt index c6901d3838..2e54ec3fe8 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex23.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex23.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.portfolio import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy @@ -29,414 +28,717 @@ import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.passes.LbePass import java.nio.file.Paths -fun complexPortfolio23(xcfa: XCFA, mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger): STM { - - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } - - var baseConfig = XcfaConfig( - inputConfig = InputConfig( - input = null, - xcfaWCtx = Triple(xcfa, mcm, parseContext), - propertyFile = null, - property = portfolioConfig.inputConfig.property), - frontendConfig = FrontendConfig( - lbeLevel = LbePass.LbeLevel.LBE_SEQ, - loopUnroll = 50, - inputType = InputType.C, - specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient)), - backendConfig = BackendConfig( - backend = Backend.CEGAR, - solverHome = portfolioConfig.backendConfig.solverHome, - timeoutMs = 0, - inProcess = false, - specConfig = CegarConfig( - initPrec = InitPrec.EMPTY, - porLevel = POR.NOPOR, - porRandomSeed = -1, - coi = ConeOfInfluenceMode.NO_COI, - cexMonitor = CexMonitorOptions.CHECK, - abstractorConfig = CegarAbstractorConfig( - abstractionSolver = "Z3", - validateAbstractionSolver = false, - domain = Domain.EXPL, - maxEnum = 1, - search = Search.ERR +fun complexPortfolio23( + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): STM { + + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } + + var baseConfig = + XcfaConfig( + inputConfig = + InputConfig( + input = null, + xcfaWCtx = Triple(xcfa, mcm, parseContext), + propertyFile = null, + property = portfolioConfig.inputConfig.property, + ), + frontendConfig = + FrontendConfig( + lbeLevel = LbePass.LbeLevel.LBE_SEQ, + loopUnroll = 50, + inputType = InputType.C, + specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient), + ), + backendConfig = + BackendConfig( + backend = Backend.CEGAR, + solverHome = portfolioConfig.backendConfig.solverHome, + timeoutMs = 0, + inProcess = false, + specConfig = + CegarConfig( + initPrec = InitPrec.EMPTY, + porLevel = POR.NOPOR, + porRandomSeed = -1, + coi = ConeOfInfluenceMode.NO_COI, + cexMonitor = CexMonitorOptions.CHECK, + abstractorConfig = + CegarAbstractorConfig( + abstractionSolver = "Z3", + validateAbstractionSolver = false, + domain = Domain.EXPL, + maxEnum = 1, + search = Search.ERR, + ), + refinerConfig = + CegarRefinerConfig( + refinementSolver = "Z3", + validateRefinementSolver = false, + refinement = Refinement.SEQ_ITP, + exprSplitter = ExprSplitterOptions.WHOLE, + pruneStrategy = PruneStrategy.FULL, ), - refinerConfig = CegarRefinerConfig( - refinementSolver = "Z3", - validateRefinementSolver = false, - refinement = Refinement.SEQ_ITP, - exprSplitter = ExprSplitterOptions.WHOLE, - pruneStrategy = PruneStrategy.FULL - ))), - outputConfig = OutputConfig( - versionInfo = false, - resultFolder = Paths.get("./").toFile(), // cwd - cOutputConfig = COutputConfig(disable = true), - witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true) + ), ), - debugConfig = portfolioConfig.debugConfig + outputConfig = + OutputConfig( + versionInfo = false, + resultFolder = Paths.get("./").toFile(), // cwd + cOutputConfig = COutputConfig(disable = true), + witnessConfig = + WitnessConfig( + disable = false, + concretizerSolver = "Z3", + validateConcretizerSolver = false, + ), + argConfig = ArgConfig(disable = true), + ), + debugConfig = portfolioConfig.debugConfig, ) - if (parseContext.multiThreading) { - val baseCegarConfig = baseConfig.backendConfig.specConfig!! - val multiThreadedCegarConfig = baseCegarConfig.copy( - coi = ConeOfInfluenceMode.COI, - porLevel = if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, - abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), - refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY) - ) - baseConfig = baseConfig.copy( - backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig)) - } - - val timeoutTrigger = ExceptionTrigger( - ErrorCodeException(ExitCodes.TIMEOUT.code), - ErrorCodeException(ExitCodes.VERIFICATION_STUCK.code), - ErrorCodeException(ExitCodes.OUT_OF_MEMORY.code), - ErrorCodeException(ExitCodes.GENERIC_ERROR.code), - label = "Timeout" + if (parseContext.multiThreading) { + val baseCegarConfig = baseConfig.backendConfig.specConfig!! + val multiThreadedCegarConfig = + baseCegarConfig.copy( + coi = ConeOfInfluenceMode.COI, + porLevel = + if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, + abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), + refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY), + ) + baseConfig = + baseConfig.copy( + backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig) + ) + } + + val timeoutTrigger = + ExceptionTrigger( + ErrorCodeException(ExitCodes.TIMEOUT.code), + ErrorCodeException(ExitCodes.VERIFICATION_STUCK.code), + ErrorCodeException(ExitCodes.OUT_OF_MEMORY.code), + ErrorCodeException(ExitCodes.GENERIC_ERROR.code), + label = "Timeout", ) - val timeoutOrSolverError = ExceptionTrigger( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - ErrorCodeException(ExitCodes.TIMEOUT.code), - ErrorCodeException(ExitCodes.VERIFICATION_STUCK.code), - ErrorCodeException(ExitCodes.OUT_OF_MEMORY.code), - ErrorCodeException(ExitCodes.GENERIC_ERROR.code), - label = "TimeoutOrSolverError" + val timeoutOrSolverError = + ExceptionTrigger( + ErrorCodeException(ExitCodes.SOLVER_ERROR.code), + ErrorCodeException(ExitCodes.TIMEOUT.code), + ErrorCodeException(ExitCodes.VERIFICATION_STUCK.code), + ErrorCodeException(ExitCodes.OUT_OF_MEMORY.code), + ErrorCodeException(ExitCodes.GENERIC_ERROR.code), + label = "TimeoutOrSolverError", ) - fun XcfaConfig<*, CegarConfig>.adaptConfig( - initPrec: InitPrec = this.backendConfig.specConfig!!.initPrec, - timeoutMs: Long = this.backendConfig.timeoutMs, - domain: Domain = this.backendConfig.specConfig!!.abstractorConfig.domain, - refinement: Refinement = this.backendConfig.specConfig!!.refinerConfig.refinement, - abstractionSolver: String = this.backendConfig.specConfig!!.abstractorConfig.abstractionSolver, - validateAbstractionSolver: Boolean = this.backendConfig.specConfig!!.abstractorConfig.validateAbstractionSolver, - refinementSolver: String = this.backendConfig.specConfig!!.refinerConfig.refinementSolver, - validateRefinementSolver: Boolean = this.backendConfig.specConfig!!.refinerConfig.validateRefinementSolver, - inProcess: Boolean = this.backendConfig.inProcess - ): XcfaConfig<*, CegarConfig> { - return copy(backendConfig = backendConfig.copy( - timeoutMs = timeoutMs, - inProcess = inProcess, - specConfig = backendConfig.specConfig!!.copy( - initPrec = initPrec, - abstractorConfig = backendConfig.specConfig!!.abstractorConfig.copy( + fun XcfaConfig<*, CegarConfig>.adaptConfig( + initPrec: InitPrec = this.backendConfig.specConfig!!.initPrec, + timeoutMs: Long = this.backendConfig.timeoutMs, + domain: Domain = this.backendConfig.specConfig!!.abstractorConfig.domain, + refinement: Refinement = this.backendConfig.specConfig!!.refinerConfig.refinement, + abstractionSolver: String = this.backendConfig.specConfig!!.abstractorConfig.abstractionSolver, + validateAbstractionSolver: Boolean = + this.backendConfig.specConfig!!.abstractorConfig.validateAbstractionSolver, + refinementSolver: String = this.backendConfig.specConfig!!.refinerConfig.refinementSolver, + validateRefinementSolver: Boolean = + this.backendConfig.specConfig!!.refinerConfig.validateRefinementSolver, + inProcess: Boolean = this.backendConfig.inProcess, + ): XcfaConfig<*, CegarConfig> { + return copy( + backendConfig = + backendConfig.copy( + timeoutMs = timeoutMs, + inProcess = inProcess, + specConfig = + backendConfig.specConfig!!.copy( + initPrec = initPrec, + abstractorConfig = + backendConfig.specConfig!! + .abstractorConfig + .copy( abstractionSolver = abstractionSolver, validateAbstractionSolver = validateAbstractionSolver, domain = domain, - ), - refinerConfig = backendConfig.specConfig!!.refinerConfig.copy( + ), + refinerConfig = + backendConfig.specConfig!! + .refinerConfig + .copy( refinementSolver = refinementSolver, validateRefinementSolver = validateRefinementSolver, refinement = refinement, - ) - ) - )) - } + ), + ), + ) + ) + } + + val quickExplConfig = baseConfig.adaptConfig(initPrec = InitPrec.ALLVARS, timeoutMs = 90_000) + val emptyExplConfig = baseConfig.adaptConfig(timeoutMs = 210_000) + val predConfig = + baseConfig.adaptConfig(domain = Domain.PRED_CART, refinement = Refinement.BW_BIN_ITP) + + fun integerStm(): STM { + fun getStm(inProcess: Boolean): STM { + val config_1_1 = + ConfigNode( + "QuickFullExpl_z3_4.10.1_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "z3:4.10.1", + refinementSolver = "z3:4.10.1", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_1 = + ConfigNode( + "EmptyExpl_z3_4.10.1_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "z3:4.10.1", + refinementSolver = "z3:4.10.1", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_3_1 = + ConfigNode( + "PredCart_z3_4.10.1_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "z3:4.10.1", + refinementSolver = "z3:4.10.1", + ), + checker, + ) - val quickExplConfig = baseConfig.adaptConfig(initPrec = InitPrec.ALLVARS, timeoutMs = 90_000) - val emptyExplConfig = baseConfig.adaptConfig(timeoutMs = 210_000) - val predConfig = baseConfig.adaptConfig(domain = Domain.PRED_CART, refinement = Refinement.BW_BIN_ITP) - - fun integerStm(): STM { - fun getStm(inProcess: Boolean): STM { - val config_1_1 = ConfigNode("QuickFullExpl_z3_4.10.1_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "z3:4.10.1", - refinementSolver = "z3:4.10.1", - refinement = Refinement.NWT_IT_WP), checker) - val config_2_1 = ConfigNode("EmptyExpl_z3_4.10.1_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "z3:4.10.1", - refinementSolver = "z3:4.10.1", - refinement = Refinement.NWT_IT_WP), checker) - val config_3_1 = ConfigNode("PredCart_z3_4.10.1_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "z3:4.10.1", - refinementSolver = "z3:4.10.1"), checker) - - val config_1_2 = ConfigNode("QuickFullExpl_Z3_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess), checker) - val config_2_2 = ConfigNode("EmptyExpl_Z3_$inProcess", emptyExplConfig.adaptConfig(inProcess = inProcess), - checker) - val config_3_2 = ConfigNode("PredCart_Z3_$inProcess", predConfig.adaptConfig(inProcess = inProcess), - checker) - - val config_1_3 = ConfigNode("QuickFullExpl_princess_2022_07_01_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "princess:2022-07-01", - refinementSolver = "princess:2022-07-01"), - checker) - val config_2_3 = ConfigNode("EmptyExpl_princess_2022_07_01_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "princess:2022-07-01", - refinementSolver = "princess:2022-07-01"), - checker) - val config_3_3 = ConfigNode("PredCart_mathsat_5.6.8_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8"), - checker) - - val config_1_4 = ConfigNode("QuickFullExpl_mathsat_5.6.8_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8"), - checker) - val config_2_4 = ConfigNode("EmptyExpl_mathsat_5.6.8_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8"), - checker) - val config_3_4 = ConfigNode("PredCart_princess_2022_07_01_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "princess:2022-07-01", - refinementSolver = "princess:2022-07-01"), - checker) - - val timeouts = setOf( - Edge(config_1_1, config_2_1, timeoutTrigger), - Edge(config_2_1, config_3_1, timeoutTrigger), - - Edge(config_1_2, config_2_2, timeoutTrigger), - Edge(config_2_2, config_3_1, timeoutTrigger), - - Edge(config_1_3, config_2_3, timeoutTrigger), - Edge(config_2_3, config_3_1, timeoutTrigger), - - Edge(config_1_4, config_2_4, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - Edge(config_2_4, config_3_1, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - ) - - val notTimeout = if (inProcess) ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError") else ExceptionTrigger(fallthroughExceptions = timeoutTrigger.exceptions, - label = "AnythingButTimeout") - - val solverExceptions = setOf( - Edge(config_1_1, config_1_2, notTimeout), - Edge(config_1_2, config_1_3, notTimeout), - Edge(config_1_3, config_1_4, notTimeout), - - Edge(config_2_1, config_2_2, notTimeout), - Edge(config_2_2, config_2_3, notTimeout), - Edge(config_2_3, config_2_4, notTimeout), - - Edge(config_3_1, config_3_2, notTimeout), - Edge(config_3_2, config_3_3, notTimeout), - Edge(config_3_3, config_3_4, notTimeout), - ) - return STM(config_1_1, timeouts union solverExceptions) - } - - val inProcess = HierarchicalNode("InProcess", - getStm(!portfolioConfig.debugConfig.debug)) // if not debug, then in process, else not in process - val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) - - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - - return STM(inProcess, setOf(fallbackEdge)) + val config_1_2 = + ConfigNode( + "QuickFullExpl_Z3_$inProcess", + quickExplConfig.adaptConfig(inProcess = inProcess), + checker, + ) + val config_2_2 = + ConfigNode( + "EmptyExpl_Z3_$inProcess", + emptyExplConfig.adaptConfig(inProcess = inProcess), + checker, + ) + val config_3_2 = + ConfigNode("PredCart_Z3_$inProcess", predConfig.adaptConfig(inProcess = inProcess), checker) + + val config_1_3 = + ConfigNode( + "QuickFullExpl_princess_2022_07_01_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "princess:2022-07-01", + refinementSolver = "princess:2022-07-01", + ), + checker, + ) + val config_2_3 = + ConfigNode( + "EmptyExpl_princess_2022_07_01_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "princess:2022-07-01", + refinementSolver = "princess:2022-07-01", + ), + checker, + ) + val config_3_3 = + ConfigNode( + "PredCart_mathsat_5.6.8_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + ), + checker, + ) + + val config_1_4 = + ConfigNode( + "QuickFullExpl_mathsat_5.6.8_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + ), + checker, + ) + val config_2_4 = + ConfigNode( + "EmptyExpl_mathsat_5.6.8_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + ), + checker, + ) + val config_3_4 = + ConfigNode( + "PredCart_princess_2022_07_01_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "princess:2022-07-01", + refinementSolver = "princess:2022-07-01", + ), + checker, + ) + + val timeouts = + setOf( + Edge(config_1_1, config_2_1, timeoutTrigger), + Edge(config_2_1, config_3_1, timeoutTrigger), + Edge(config_1_2, config_2_2, timeoutTrigger), + Edge(config_2_2, config_3_1, timeoutTrigger), + Edge(config_1_3, config_2_3, timeoutTrigger), + Edge(config_2_3, config_3_1, timeoutTrigger), + Edge( + config_1_4, + config_2_4, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + Edge( + config_2_4, + config_3_1, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + ) + + val notTimeout = + if (inProcess) + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") + else + ExceptionTrigger( + fallthroughExceptions = timeoutTrigger.exceptions, + label = "AnythingButTimeout", + ) + + val solverExceptions = + setOf( + Edge(config_1_1, config_1_2, notTimeout), + Edge(config_1_2, config_1_3, notTimeout), + Edge(config_1_3, config_1_4, notTimeout), + Edge(config_2_1, config_2_2, notTimeout), + Edge(config_2_2, config_2_3, notTimeout), + Edge(config_2_3, config_2_4, notTimeout), + Edge(config_3_1, config_3_2, notTimeout), + Edge(config_3_2, config_3_3, notTimeout), + Edge(config_3_3, config_3_4, notTimeout), + ) + return STM(config_1_1, timeouts union solverExceptions) } - fun bitwiseStm(): STM { - fun getStm(inProcess: Boolean): STM { - val config_1_1 = ConfigNode("QuickFullExpl_Z3_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, refinement = Refinement.NWT_IT_WP), checker) - val config_2_1 = ConfigNode("EmptyExpl_Z3_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, refinement = Refinement.NWT_IT_WP), - checker) - val config_3_1 = ConfigNode("PredCart_mathsat_5.6.8_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8"), - checker) - - val config_1_2 = ConfigNode("QuickFullExpl_cvc5_1.0.2_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - val config_2_2 = ConfigNode("EmptyExpl_cvc5_1.0.2_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - val config_3_2 = ConfigNode("PredCart_z3_4.10.1_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "z3:4.10.1", - refinementSolver = "z3:4.10.1"), checker) - - val config_1_3 = ConfigNode("QuickFullExpl_mathsat_5.6.8_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8", - refinement = Refinement.NWT_IT_WP), checker) - val config_2_3 = ConfigNode("EmptyExpl_mathsat_5.6.8_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8", - refinement = Refinement.SEQ_ITP), checker) - val config_3_3 = ConfigNode("PredCart_cvc5_1.0.2_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - - val timeouts = setOf( - Edge(config_1_1, config_2_1, timeoutTrigger), - Edge(config_2_1, config_3_1, timeoutTrigger), - - Edge(config_1_2, config_2_2, timeoutTrigger), - Edge(config_2_2, config_3_1, timeoutTrigger), - - Edge(config_1_3, config_2_3, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - Edge(config_2_3, config_3_1, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - ) - - val notTimeout = if (inProcess) ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError") else ExceptionTrigger(fallthroughExceptions = timeoutTrigger.exceptions, - label = "AnythingButTimeout") - - val solverExceptions = setOf( - Edge(config_1_1, config_1_2, notTimeout), - Edge(config_1_2, config_1_3, notTimeout), - - Edge(config_2_1, config_2_2, notTimeout), - Edge(config_2_2, config_2_3, notTimeout), - - Edge(config_3_1, config_3_2, notTimeout), - Edge(config_3_2, config_3_3, notTimeout), - ) - return STM(config_1_1, timeouts union solverExceptions) - } - - val inProcess = HierarchicalNode("InProcess", - getStm(!portfolioConfig.debugConfig.debug)) // if not debug, then in process, else not in process - val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) - - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - - return STM(inProcess, setOf(fallbackEdge)) + val inProcess = + HierarchicalNode( + "InProcess", + getStm(!portfolioConfig.debugConfig.debug), + ) // if not debug, then in process, else not in process + val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) + + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) + + return STM(inProcess, setOf(fallbackEdge)) + } + + fun bitwiseStm(): STM { + fun getStm(inProcess: Boolean): STM { + val config_1_1 = + ConfigNode( + "QuickFullExpl_Z3_$inProcess", + quickExplConfig.adaptConfig(inProcess = inProcess, refinement = Refinement.NWT_IT_WP), + checker, + ) + val config_2_1 = + ConfigNode( + "EmptyExpl_Z3_$inProcess", + emptyExplConfig.adaptConfig(inProcess = inProcess, refinement = Refinement.NWT_IT_WP), + checker, + ) + val config_3_1 = + ConfigNode( + "PredCart_mathsat_5.6.8_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + ), + checker, + ) + + val config_1_2 = + ConfigNode( + "QuickFullExpl_cvc5_1.0.2_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_2 = + ConfigNode( + "EmptyExpl_cvc5_1.0.2_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_3_2 = + ConfigNode( + "PredCart_z3_4.10.1_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "z3:4.10.1", + refinementSolver = "z3:4.10.1", + ), + checker, + ) + + val config_1_3 = + ConfigNode( + "QuickFullExpl_mathsat_5.6.8_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_3 = + ConfigNode( + "EmptyExpl_mathsat_5.6.8_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + refinement = Refinement.SEQ_ITP, + ), + checker, + ) + val config_3_3 = + ConfigNode( + "PredCart_cvc5_1.0.2_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + + val timeouts = + setOf( + Edge(config_1_1, config_2_1, timeoutTrigger), + Edge(config_2_1, config_3_1, timeoutTrigger), + Edge(config_1_2, config_2_2, timeoutTrigger), + Edge(config_2_2, config_3_1, timeoutTrigger), + Edge( + config_1_3, + config_2_3, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + Edge( + config_2_3, + config_3_1, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + ) + + val notTimeout = + if (inProcess) + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") + else + ExceptionTrigger( + fallthroughExceptions = timeoutTrigger.exceptions, + label = "AnythingButTimeout", + ) + + val solverExceptions = + setOf( + Edge(config_1_1, config_1_2, notTimeout), + Edge(config_1_2, config_1_3, notTimeout), + Edge(config_2_1, config_2_2, notTimeout), + Edge(config_2_2, config_2_3, notTimeout), + Edge(config_3_1, config_3_2, notTimeout), + Edge(config_3_2, config_3_3, notTimeout), + ) + return STM(config_1_1, timeouts union solverExceptions) } - fun floatsStm(): STM { - fun getStm(inProcess: Boolean): STM { - val config_1_1 = ConfigNode("QuickFullExpl_cvc5_1.0.2_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - val config_2_1 = ConfigNode("EmptyExpl_cvc5_1.0.2_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - val config_3_1 = ConfigNode("PredCart_mathsat_5.6.8_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8"), - checker) - - val config_1_2 = ConfigNode("QuickFullExpl_cvc5_1.0.2_seq_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.SEQ_ITP), checker) - val config_2_2 = ConfigNode("EmptyExpl_cvc5_1.0.2_seq_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.SEQ_ITP), checker) - val config_3_2 = ConfigNode("PredCart_bitwuzla_latest_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "bitwuzla:latest", - refinementSolver = "bitwuzla:latest", - refinement = Refinement.NWT_IT_WP), checker) - - val config_1_3 = ConfigNode("QuickFullExpl_mathsat_5.6.8_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8", - validateAbstractionSolver = true, validateRefinementSolver = true, - refinement = Refinement.NWT_IT_WP), - checker) - val config_2_3 = ConfigNode("EmptyExpl_mathsat_5.6.8_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:5.6.8", - refinementSolver = "mathsat:5.6.8", - validateAbstractionSolver = true, validateRefinementSolver = true, - refinement = Refinement.NWT_IT_WP), - checker) - val config_3_3 = ConfigNode("PredCart_cvc5_1.0.2_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "cvc5:1.0.2", - refinementSolver = "cvc5:1.0.2", - refinement = Refinement.NWT_IT_WP), checker) - - val config_1_4 = ConfigNode("QuickFullExpl_mathsat_fp_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:fp", - refinementSolver = "mathsat:fp", - validateAbstractionSolver = true, validateRefinementSolver = true), checker) - val config_2_4 = ConfigNode("EmptyExpl_mathsat_fp_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:fp", - refinementSolver = "mathsat:fp", - validateAbstractionSolver = true, validateRefinementSolver = true), checker) - val config_3_4 = ConfigNode("PredCart_mathsat_fp_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "mathsat:fp", - refinementSolver = "mathsat:fp", - validateAbstractionSolver = true, validateRefinementSolver = true), checker) - - val config_1_5 = ConfigNode("QuickFullExpl_Z3_$inProcess", - quickExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "Z3", refinementSolver = "Z3", - validateAbstractionSolver = true, - validateRefinementSolver = true, refinement = Refinement.NWT_IT_WP), checker) - val config_2_5 = ConfigNode("EmptyExpl_Z3_$inProcess", - emptyExplConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "Z3", refinementSolver = "Z3", - validateAbstractionSolver = true, - validateRefinementSolver = true, refinement = Refinement.NWT_IT_WP), checker) - val config_3_5 = ConfigNode("PredCart_Z3_$inProcess", - predConfig.adaptConfig(inProcess = inProcess, abstractionSolver = "Z3", refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP), - checker) - - val timeouts = setOf( - Edge(config_1_1, config_2_1, timeoutTrigger), - Edge(config_2_1, config_3_1, timeoutTrigger), - - Edge(config_1_2, config_2_2, timeoutTrigger), - Edge(config_2_2, config_3_1, timeoutTrigger), - - Edge(config_1_3, config_2_3, timeoutTrigger), - Edge(config_2_3, config_3_1, timeoutTrigger), - - Edge(config_1_4, config_2_4, timeoutTrigger), - Edge(config_2_4, config_3_1, timeoutTrigger), - - Edge(config_1_5, config_2_5, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - Edge(config_2_5, config_3_1, - if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything")), - ) - - val notTimeout = if (inProcess) ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError") else ExceptionTrigger(fallthroughExceptions = timeoutTrigger.exceptions, - label = "AnythingButTimeout") - - val solverExceptions = setOf( - Edge(config_1_1, config_1_2, notTimeout), - Edge(config_1_2, config_1_3, notTimeout), - Edge(config_1_3, config_1_4, notTimeout), - Edge(config_1_4, config_1_5, notTimeout), - - Edge(config_2_1, config_2_2, notTimeout), - Edge(config_2_2, config_2_3, notTimeout), - Edge(config_2_3, config_2_4, notTimeout), - Edge(config_2_4, config_2_5, notTimeout), - - Edge(config_3_1, config_3_2, notTimeout), - Edge(config_3_2, config_3_3, notTimeout), - Edge(config_3_3, config_3_4, notTimeout), - Edge(config_3_4, config_3_5, notTimeout), - ) - return STM(config_1_1, timeouts union solverExceptions) - } - - val inProcess = HierarchicalNode("InProcess", - getStm(!portfolioConfig.debugConfig.debug)) // if not debug, then in process, else not in process - val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) - - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - - return STM(inProcess, setOf(fallbackEdge)) + val inProcess = + HierarchicalNode( + "InProcess", + getStm(!portfolioConfig.debugConfig.debug), + ) // if not debug, then in process, else not in process + val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) + + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) + + return STM(inProcess, setOf(fallbackEdge)) + } + + fun floatsStm(): STM { + fun getStm(inProcess: Boolean): STM { + val config_1_1 = + ConfigNode( + "QuickFullExpl_cvc5_1.0.2_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_1 = + ConfigNode( + "EmptyExpl_cvc5_1.0.2_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_3_1 = + ConfigNode( + "PredCart_mathsat_5.6.8_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + ), + checker, + ) + + val config_1_2 = + ConfigNode( + "QuickFullExpl_cvc5_1.0.2_seq_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.SEQ_ITP, + ), + checker, + ) + val config_2_2 = + ConfigNode( + "EmptyExpl_cvc5_1.0.2_seq_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.SEQ_ITP, + ), + checker, + ) + val config_3_2 = + ConfigNode( + "PredCart_bitwuzla_latest_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "bitwuzla:latest", + refinementSolver = "bitwuzla:latest", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + + val config_1_3 = + ConfigNode( + "QuickFullExpl_mathsat_5.6.8_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + validateAbstractionSolver = true, + validateRefinementSolver = true, + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_3 = + ConfigNode( + "EmptyExpl_mathsat_5.6.8_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:5.6.8", + refinementSolver = "mathsat:5.6.8", + validateAbstractionSolver = true, + validateRefinementSolver = true, + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_3_3 = + ConfigNode( + "PredCart_cvc5_1.0.2_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "cvc5:1.0.2", + refinementSolver = "cvc5:1.0.2", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + + val config_1_4 = + ConfigNode( + "QuickFullExpl_mathsat_fp_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:fp", + refinementSolver = "mathsat:fp", + validateAbstractionSolver = true, + validateRefinementSolver = true, + ), + checker, + ) + val config_2_4 = + ConfigNode( + "EmptyExpl_mathsat_fp_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:fp", + refinementSolver = "mathsat:fp", + validateAbstractionSolver = true, + validateRefinementSolver = true, + ), + checker, + ) + val config_3_4 = + ConfigNode( + "PredCart_mathsat_fp_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "mathsat:fp", + refinementSolver = "mathsat:fp", + validateAbstractionSolver = true, + validateRefinementSolver = true, + ), + checker, + ) + + val config_1_5 = + ConfigNode( + "QuickFullExpl_Z3_$inProcess", + quickExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "Z3", + refinementSolver = "Z3", + validateAbstractionSolver = true, + validateRefinementSolver = true, + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_2_5 = + ConfigNode( + "EmptyExpl_Z3_$inProcess", + emptyExplConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "Z3", + refinementSolver = "Z3", + validateAbstractionSolver = true, + validateRefinementSolver = true, + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + val config_3_5 = + ConfigNode( + "PredCart_Z3_$inProcess", + predConfig.adaptConfig( + inProcess = inProcess, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + ), + checker, + ) + + val timeouts = + setOf( + Edge(config_1_1, config_2_1, timeoutTrigger), + Edge(config_2_1, config_3_1, timeoutTrigger), + Edge(config_1_2, config_2_2, timeoutTrigger), + Edge(config_2_2, config_3_1, timeoutTrigger), + Edge(config_1_3, config_2_3, timeoutTrigger), + Edge(config_2_3, config_3_1, timeoutTrigger), + Edge(config_1_4, config_2_4, timeoutTrigger), + Edge(config_2_4, config_3_1, timeoutTrigger), + Edge( + config_1_5, + config_2_5, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + Edge( + config_2_5, + config_3_1, + if (inProcess) timeoutOrSolverError else ExceptionTrigger(label = "Anything"), + ), + ) + + val notTimeout = + if (inProcess) + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") + else + ExceptionTrigger( + fallthroughExceptions = timeoutTrigger.exceptions, + label = "AnythingButTimeout", + ) + + val solverExceptions = + setOf( + Edge(config_1_1, config_1_2, notTimeout), + Edge(config_1_2, config_1_3, notTimeout), + Edge(config_1_3, config_1_4, notTimeout), + Edge(config_1_4, config_1_5, notTimeout), + Edge(config_2_1, config_2_2, notTimeout), + Edge(config_2_2, config_2_3, notTimeout), + Edge(config_2_3, config_2_4, notTimeout), + Edge(config_2_4, config_2_5, notTimeout), + Edge(config_3_1, config_3_2, notTimeout), + Edge(config_3_2, config_3_3, notTimeout), + Edge(config_3_3, config_3_4, notTimeout), + Edge(config_3_4, config_3_5, notTimeout), + ) + return STM(config_1_1, timeouts union solverExceptions) } - return if (parseContext.arithmeticTraits.contains(ArithmeticTrait.FLOAT)) floatsStm() - else if (parseContext.arithmeticTraits.contains(ArithmeticTrait.BITWISE)) bitwiseStm() - else integerStm() + val inProcess = + HierarchicalNode( + "InProcess", + getStm(!portfolioConfig.debugConfig.debug), + ) // if not debug, then in process, else not in process + val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) + + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) + + return STM(inProcess, setOf(fallbackEdge)) + } + + return if (parseContext.arithmeticTraits.contains(ArithmeticTrait.FLOAT)) floatsStm() + else if (parseContext.arithmeticTraits.contains(ArithmeticTrait.BITWISE)) bitwiseStm() + else integerStm() } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex24.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex24.kt index ca6f147553..7547273b57 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex24.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex24.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.portfolio import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy @@ -32,636 +31,1059 @@ import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass import java.nio.file.Paths fun complexPortfolio24( - xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger): STM { + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): STM { - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } - var baseConfig = XcfaConfig( - inputConfig = InputConfig( - input = null, - xcfaWCtx = Triple(xcfa, mcm, parseContext), - propertyFile = null, - property = portfolioConfig.inputConfig.property), - frontendConfig = FrontendConfig( - lbeLevel = LbePass.level, - loopUnroll = LoopUnrollPass.UNROLL_LIMIT, - inputType = InputType.C, - specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient)), - backendConfig = BackendConfig( - backend = Backend.CEGAR, - solverHome = portfolioConfig.backendConfig.solverHome, - timeoutMs = 0, - specConfig = CegarConfig( - initPrec = InitPrec.EMPTY, - porLevel = POR.NOPOR, - porRandomSeed = -1, - coi = ConeOfInfluenceMode.NO_COI, - cexMonitor = CexMonitorOptions.CHECK, - abstractorConfig = CegarAbstractorConfig( - abstractionSolver = "Z3", - validateAbstractionSolver = false, - domain = Domain.EXPL, - maxEnum = 1, - search = Search.ERR + var baseConfig = + XcfaConfig( + inputConfig = + InputConfig( + input = null, + xcfaWCtx = Triple(xcfa, mcm, parseContext), + propertyFile = null, + property = portfolioConfig.inputConfig.property, + ), + frontendConfig = + FrontendConfig( + lbeLevel = LbePass.level, + loopUnroll = LoopUnrollPass.UNROLL_LIMIT, + inputType = InputType.C, + specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient), + ), + backendConfig = + BackendConfig( + backend = Backend.CEGAR, + solverHome = portfolioConfig.backendConfig.solverHome, + timeoutMs = 0, + specConfig = + CegarConfig( + initPrec = InitPrec.EMPTY, + porLevel = POR.NOPOR, + porRandomSeed = -1, + coi = ConeOfInfluenceMode.NO_COI, + cexMonitor = CexMonitorOptions.CHECK, + abstractorConfig = + CegarAbstractorConfig( + abstractionSolver = "Z3", + validateAbstractionSolver = false, + domain = Domain.EXPL, + maxEnum = 1, + search = Search.ERR, + ), + refinerConfig = + CegarRefinerConfig( + refinementSolver = "Z3", + validateRefinementSolver = false, + refinement = Refinement.SEQ_ITP, + exprSplitter = ExprSplitterOptions.WHOLE, + pruneStrategy = PruneStrategy.FULL, ), - refinerConfig = CegarRefinerConfig( - refinementSolver = "Z3", - validateRefinementSolver = false, - refinement = Refinement.SEQ_ITP, - exprSplitter = ExprSplitterOptions.WHOLE, - pruneStrategy = PruneStrategy.FULL - ))), - outputConfig = OutputConfig( - versionInfo = false, - resultFolder = Paths.get("./").toFile(), // cwd - cOutputConfig = COutputConfig(disable = true), - witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true), - enableOutput = portfolioConfig.outputConfig.enableOutput, - ), - debugConfig = portfolioConfig.debugConfig + ), + ), + outputConfig = + OutputConfig( + versionInfo = false, + resultFolder = Paths.get("./").toFile(), // cwd + cOutputConfig = COutputConfig(disable = true), + witnessConfig = + WitnessConfig( + disable = false, + concretizerSolver = "Z3", + validateConcretizerSolver = false, + ), + argConfig = ArgConfig(disable = true), + enableOutput = portfolioConfig.outputConfig.enableOutput, + ), + debugConfig = portfolioConfig.debugConfig, ) - if (parseContext.multiThreading) { - val baseCegarConfig = baseConfig.backendConfig.specConfig!! - val multiThreadedCegarConfig = baseCegarConfig.copy( - coi = if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) ConeOfInfluenceMode.NO_COI else ConeOfInfluenceMode.COI, - porLevel = if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, - abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.DFS), - ) - baseConfig = baseConfig.copy( - backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig)) - } + if (parseContext.multiThreading) { + val baseCegarConfig = baseConfig.backendConfig.specConfig!! + val multiThreadedCegarConfig = + baseCegarConfig.copy( + coi = + if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) + ConeOfInfluenceMode.NO_COI + else ConeOfInfluenceMode.COI, + porLevel = + if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, + abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.DFS), + ) + baseConfig = + baseConfig.copy( + backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig) + ) + } - if (!xcfa.isInlined) { - val baseCegarConfig = baseConfig.backendConfig.specConfig!! - val recursiveConfig = baseCegarConfig.copy( - abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), - refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY) - ) - baseConfig = baseConfig.copy(backendConfig = baseConfig.backendConfig.copy(specConfig = recursiveConfig)) - } + if (!xcfa.isInlined) { + val baseCegarConfig = baseConfig.backendConfig.specConfig!! + val recursiveConfig = + baseCegarConfig.copy( + abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), + refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY), + ) + baseConfig = + baseConfig.copy(backendConfig = baseConfig.backendConfig.copy(specConfig = recursiveConfig)) + } - val timeoutOrNotSolvableError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - ErrorCodeException(ExitCodes.SERVER_ERROR.code), + val timeoutOrNotSolvableError = + ExceptionTrigger( + fallthroughExceptions = + setOf( + ErrorCodeException(ExitCodes.SOLVER_ERROR.code), + ErrorCodeException(ExitCodes.SERVER_ERROR.code), ), - label = "TimeoutOrNotSolvableError" + label = "TimeoutOrNotSolvableError", ) - val timeoutOrSolverError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SERVER_ERROR.code), - ), - label = "TimeoutOrSolverError" + val timeoutOrSolverError = + ExceptionTrigger( + fallthroughExceptions = setOf(ErrorCodeException(ExitCodes.SERVER_ERROR.code)), + label = "TimeoutOrSolverError", ) - val solverError = ExceptionTrigger( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError" - ) + val solverError = + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") - val anyError = ExceptionTrigger(label = "Anything") + val anyError = ExceptionTrigger(label = "Anything") - fun XcfaConfig<*, CegarConfig>.adaptConfig( - initPrec: InitPrec = this.backendConfig.specConfig!!.initPrec, - timeoutMs: Long = this.backendConfig.timeoutMs, - domain: Domain = this.backendConfig.specConfig!!.abstractorConfig.domain, - refinement: Refinement = this.backendConfig.specConfig!!.refinerConfig.refinement, - abstractionSolver: String = this.backendConfig.specConfig!!.abstractorConfig.abstractionSolver, - validateAbstractionSolver: Boolean = this.backendConfig.specConfig!!.abstractorConfig.validateAbstractionSolver, - refinementSolver: String = this.backendConfig.specConfig!!.refinerConfig.refinementSolver, - validateRefinementSolver: Boolean = this.backendConfig.specConfig!!.refinerConfig.validateRefinementSolver, - inProcess: Boolean = this.backendConfig.inProcess - ): XcfaConfig<*, CegarConfig> { - return copy(backendConfig = backendConfig.copy( - timeoutMs = timeoutMs, - inProcess = inProcess, - specConfig = backendConfig.specConfig!!.copy( - initPrec = initPrec, - abstractorConfig = backendConfig.specConfig!!.abstractorConfig.copy( + fun XcfaConfig<*, CegarConfig>.adaptConfig( + initPrec: InitPrec = this.backendConfig.specConfig!!.initPrec, + timeoutMs: Long = this.backendConfig.timeoutMs, + domain: Domain = this.backendConfig.specConfig!!.abstractorConfig.domain, + refinement: Refinement = this.backendConfig.specConfig!!.refinerConfig.refinement, + abstractionSolver: String = this.backendConfig.specConfig!!.abstractorConfig.abstractionSolver, + validateAbstractionSolver: Boolean = + this.backendConfig.specConfig!!.abstractorConfig.validateAbstractionSolver, + refinementSolver: String = this.backendConfig.specConfig!!.refinerConfig.refinementSolver, + validateRefinementSolver: Boolean = + this.backendConfig.specConfig!!.refinerConfig.validateRefinementSolver, + inProcess: Boolean = this.backendConfig.inProcess, + ): XcfaConfig<*, CegarConfig> { + return copy( + backendConfig = + backendConfig.copy( + timeoutMs = timeoutMs, + inProcess = inProcess, + specConfig = + backendConfig.specConfig!!.copy( + initPrec = initPrec, + abstractorConfig = + backendConfig.specConfig!! + .abstractorConfig + .copy( abstractionSolver = abstractionSolver, validateAbstractionSolver = validateAbstractionSolver, domain = domain, - ), - refinerConfig = backendConfig.specConfig!!.refinerConfig.copy( + ), + refinerConfig = + backendConfig.specConfig!! + .refinerConfig + .copy( refinementSolver = refinementSolver, validateRefinementSolver = validateRefinementSolver, refinement = refinement, - ) - ) - )) - } + ), + ), + ) + ) + } - fun getStm(trait: ArithmeticTrait, inProcess: Boolean): STM { - val edges = LinkedHashSet() - val config_BITWISE_EXPL_NWT_IT_WP_cvc5 = ConfigNode("BITWISE_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - val config_BITWISE_EXPL_NWT_IT_WP_Z3 = ConfigNode("BITWISE_EXPL_NWT_IT_WP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_cvc5, config_BITWISE_EXPL_NWT_IT_WP_Z3, solverError)) - val config_BITWISE_EXPL_NWT_IT_WP_mathsat = ConfigNode("BITWISE_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_Z3, config_BITWISE_EXPL_NWT_IT_WP_mathsat, solverError)) - val config_BITWISE_PRED_CART_SEQ_ITP_mathsat = ConfigNode("BITWISE_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_cvc5, config_BITWISE_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_Z3, config_BITWISE_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_mathsat, config_BITWISE_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_BITWISE_PRED_CART_SEQ_ITP_cvc5 = ConfigNode("BITWISE_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_BITWISE_PRED_CART_SEQ_ITP_mathsat, config_BITWISE_PRED_CART_SEQ_ITP_cvc5, solverError)) - val config_BITWISE_EXPL_SEQ_ITP_mathsat = ConfigNode("BITWISE_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_BITWISE_PRED_CART_SEQ_ITP_mathsat, config_BITWISE_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_BITWISE_PRED_CART_SEQ_ITP_cvc5, config_BITWISE_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_BITWISE_EXPL_SEQ_ITP_cvc5 = ConfigNode("BITWISE_EXPL_SEQ_ITP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_BITWISE_EXPL_SEQ_ITP_mathsat, config_BITWISE_EXPL_SEQ_ITP_cvc5, solverError)) - val config_FLOAT_EXPL_NWT_IT_WP_cvc5 = ConfigNode("FLOAT_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 200000 - ), checker) - val config_FLOAT_EXPL_NWT_IT_WP_Z3 = ConfigNode("FLOAT_EXPL_NWT_IT_WP_Z3-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 200000 - ), checker) - edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_cvc5, config_FLOAT_EXPL_NWT_IT_WP_Z3, solverError)) - val config_FLOAT_EXPL_NWT_IT_WP_mathsat = ConfigNode("FLOAT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", validateRefinementSolver = true, - refinement = Refinement.NWT_IT_WP, - timeoutMs = 200000 - ), checker) - edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_Z3, config_FLOAT_EXPL_NWT_IT_WP_mathsat, solverError)) - val config_FLOAT_PRED_CART_SEQ_ITP_mathsat = ConfigNode("FLOAT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", validateRefinementSolver = true, - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_cvc5, config_FLOAT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_Z3, config_FLOAT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_mathsat, config_FLOAT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_FLOAT_PRED_CART_SEQ_ITP_cvc5 = ConfigNode("FLOAT_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_FLOAT_PRED_CART_SEQ_ITP_mathsat, config_FLOAT_PRED_CART_SEQ_ITP_cvc5, solverError)) - val config_FLOAT_EXPL_SEQ_ITP_mathsat = ConfigNode("FLOAT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", validateRefinementSolver = true, - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_FLOAT_PRED_CART_SEQ_ITP_mathsat, config_FLOAT_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_FLOAT_PRED_CART_SEQ_ITP_cvc5, config_FLOAT_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_FLOAT_EXPL_SEQ_ITP_cvc5 = ConfigNode("FLOAT_EXPL_SEQ_ITP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_FLOAT_EXPL_SEQ_ITP_mathsat, config_FLOAT_EXPL_SEQ_ITP_cvc5, solverError)) - val config_LIN_INT_EXPL_NWT_IT_WP_mathsat = ConfigNode("LIN_INT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - val config_LIN_INT_EXPL_NWT_IT_WP_Z3 = ConfigNode("LIN_INT_EXPL_NWT_IT_WP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, config_LIN_INT_EXPL_NWT_IT_WP_Z3, solverError)) - val config_LIN_INT_EXPL_SEQ_ITP_Z3 = ConfigNode("LIN_INT_EXPL_SEQ_ITP_Z3-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, config_LIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_LIN_INT_EXPL_NWT_IT_WP_Z3, config_LIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_LIN_INT_EXPL_SEQ_ITP_mathsat = ConfigNode("LIN_INT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_LIN_INT_EXPL_SEQ_ITP_Z3, config_LIN_INT_EXPL_SEQ_ITP_mathsat, solverError)) - val config_LIN_INT_PRED_CART_SEQ_ITP_Z3 = ConfigNode("LIN_INT_PRED_CART_SEQ_ITP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_LIN_INT_EXPL_SEQ_ITP_Z3, config_LIN_INT_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_LIN_INT_EXPL_SEQ_ITP_mathsat, config_LIN_INT_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_LIN_INT_PRED_CART_SEQ_ITP_mathsat = ConfigNode("LIN_INT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_LIN_INT_PRED_CART_SEQ_ITP_Z3, config_LIN_INT_PRED_CART_SEQ_ITP_mathsat, solverError)) - val config_LIN_INT_PRED_CART_SEQ_ITP_z3 = ConfigNode("LIN_INT_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "z3:4.12.2", - refinementSolver = "z3:4.12.2", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_LIN_INT_PRED_CART_SEQ_ITP_mathsat, config_LIN_INT_PRED_CART_SEQ_ITP_z3, solverError)) - val config_NONLIN_INT_EXPL_NWT_IT_WP_Z3 = ConfigNode("NONLIN_INT_EXPL_NWT_IT_WP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - val config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat = ConfigNode("NONLIN_INT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat, solverError)) - val config_NONLIN_INT_EXPL_SEQ_ITP_Z3 = ConfigNode("NONLIN_INT_EXPL_SEQ_ITP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, config_NONLIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat, config_NONLIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_NONLIN_INT_EXPL_SEQ_ITP_z3 = ConfigNode("NONLIN_INT_EXPL_SEQ_ITP_z3:4.12.2-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "z3:4.12.2", - refinementSolver = "z3:4.12.2", - refinement = Refinement.SEQ_ITP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_NONLIN_INT_EXPL_SEQ_ITP_Z3, config_NONLIN_INT_EXPL_SEQ_ITP_z3, solverError)) - val config_NONLIN_INT_EXPL_SEQ_ITP_mathsat = ConfigNode("NONLIN_INT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 200000 - ), checker) - edges.add(Edge(config_NONLIN_INT_EXPL_SEQ_ITP_Z3, config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_NONLIN_INT_EXPL_SEQ_ITP_z3, config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat = ConfigNode( - "NONLIN_INT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutOrSolverError else anyError)) - val config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3 = ConfigNode("NONLIN_INT_PRED_CART_SEQ_ITP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add( - Edge(config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3, solverError)) - val config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5 = ConfigNode("NONLIN_INT_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3, config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5, - if (inProcess) timeoutOrSolverError else anyError)) - val config_ARR_EXPL_NWT_IT_WP_cvc5 = ConfigNode("ARR_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - val config_ARR_EXPL_NWT_IT_WP_Z3 = ConfigNode("ARR_EXPL_NWT_IT_WP_Z3-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 100000 - ), checker) - edges.add(Edge(config_ARR_EXPL_NWT_IT_WP_cvc5, config_ARR_EXPL_NWT_IT_WP_Z3, solverError)) - val config_ARR_PRED_CART_SEQ_ITP_Z3 = ConfigNode("ARR_PRED_CART_SEQ_ITP_Z3-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_ARR_EXPL_NWT_IT_WP_cvc5, config_ARR_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_ARR_EXPL_NWT_IT_WP_Z3, config_ARR_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_ARR_PRED_CART_SEQ_ITP_z3 = ConfigNode("ARR_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "z3:4.12.2", - refinementSolver = "z3:4.12.2", - refinement = Refinement.SEQ_ITP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_Z3, config_ARR_PRED_CART_SEQ_ITP_z3, solverError)) - val config_ARR_PRED_CART_SEQ_ITP_princess = ConfigNode("ARR_PRED_CART_SEQ_ITP_princess:2023-06-19-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "princess:2023-06-19", - refinementSolver = "princess:2023-06-19", - refinement = Refinement.SEQ_ITP, - timeoutMs = 500000 - ), checker) - edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_Z3, config_ARR_PRED_CART_SEQ_ITP_princess, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_z3, config_ARR_PRED_CART_SEQ_ITP_princess, - if (inProcess) timeoutOrSolverError else anyError)) - val config_ARR_PRED_CART_SEQ_ITP_cvc5 = ConfigNode("ARR_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "cvc5:1.0.8", - refinementSolver = "cvc5:1.0.8", - refinement = Refinement.SEQ_ITP, - timeoutMs = 500000 - ), checker) - edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_princess, config_ARR_PRED_CART_SEQ_ITP_cvc5, solverError)) - val config_MULTITHREAD_EXPL_SEQ_ITP_Z3 = ConfigNode("MULTITHREAD_EXPL_SEQ_ITP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 150000 - ), checker) - val config_MULTITHREAD_EXPL_SEQ_ITP_mathsat = ConfigNode("MULTITHREAD_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 150000 - ), checker) - edges.add(Edge(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, config_MULTITHREAD_EXPL_SEQ_ITP_mathsat, solverError)) - val config_MULTITHREAD_EXPL_NWT_IT_WP_z3 = ConfigNode("MULTITHREAD_EXPL_NWT_IT_WP_z3:4.12.2-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "z3:4.12.2", - refinementSolver = "z3:4.12.2", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, config_MULTITHREAD_EXPL_NWT_IT_WP_z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_MULTITHREAD_EXPL_SEQ_ITP_mathsat, config_MULTITHREAD_EXPL_NWT_IT_WP_z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat = ConfigNode( - "MULTITHREAD_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.EXPL, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.NWT_IT_WP, - timeoutMs = 300000 - ), checker) - edges.add(Edge(config_MULTITHREAD_EXPL_NWT_IT_WP_z3, config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat, solverError)) - val config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3 = ConfigNode("MULTITHREAD_PRED_CART_SEQ_ITP_Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "Z3", - refinementSolver = "Z3", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add(Edge(config_MULTITHREAD_EXPL_NWT_IT_WP_z3, config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrNotSolvableError else anyError)) - edges.add(Edge(config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat, config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutOrSolverError else anyError)) - val config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat = ConfigNode( - "MULTITHREAD_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "mathsat:5.6.10", - refinementSolver = "mathsat:5.6.10", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add( - Edge(config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat, solverError)) - val config_MULTITHREAD_PRED_CART_SEQ_ITP_z3 = ConfigNode("MULTITHREAD_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - domain = Domain.PRED_CART, - abstractionSolver = "z3:4.12.2", - refinementSolver = "z3:4.12.2", - refinement = Refinement.SEQ_ITP, - timeoutMs = 0 - ), checker) - edges.add( - Edge(config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat, config_MULTITHREAD_PRED_CART_SEQ_ITP_z3, solverError)) - if (trait == ArithmeticTrait.BITWISE) { - return STM(config_BITWISE_EXPL_NWT_IT_WP_cvc5, edges) - } + fun getStm(trait: ArithmeticTrait, inProcess: Boolean): STM { + val edges = LinkedHashSet() + val config_BITWISE_EXPL_NWT_IT_WP_cvc5 = + ConfigNode( + "BITWISE_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + val config_BITWISE_EXPL_NWT_IT_WP_Z3 = + ConfigNode( + "BITWISE_EXPL_NWT_IT_WP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge(config_BITWISE_EXPL_NWT_IT_WP_cvc5, config_BITWISE_EXPL_NWT_IT_WP_Z3, solverError) + ) + val config_BITWISE_EXPL_NWT_IT_WP_mathsat = + ConfigNode( + "BITWISE_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge(config_BITWISE_EXPL_NWT_IT_WP_Z3, config_BITWISE_EXPL_NWT_IT_WP_mathsat, solverError) + ) + val config_BITWISE_PRED_CART_SEQ_ITP_mathsat = + ConfigNode( + "BITWISE_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_BITWISE_EXPL_NWT_IT_WP_cvc5, + config_BITWISE_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_BITWISE_EXPL_NWT_IT_WP_Z3, + config_BITWISE_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_BITWISE_EXPL_NWT_IT_WP_mathsat, + config_BITWISE_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_BITWISE_PRED_CART_SEQ_ITP_cvc5 = + ConfigNode( + "BITWISE_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_BITWISE_PRED_CART_SEQ_ITP_mathsat, + config_BITWISE_PRED_CART_SEQ_ITP_cvc5, + solverError, + ) + ) + val config_BITWISE_EXPL_SEQ_ITP_mathsat = + ConfigNode( + "BITWISE_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_BITWISE_PRED_CART_SEQ_ITP_mathsat, + config_BITWISE_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_BITWISE_PRED_CART_SEQ_ITP_cvc5, + config_BITWISE_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_BITWISE_EXPL_SEQ_ITP_cvc5 = + ConfigNode( + "BITWISE_EXPL_SEQ_ITP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge(config_BITWISE_EXPL_SEQ_ITP_mathsat, config_BITWISE_EXPL_SEQ_ITP_cvc5, solverError) + ) + val config_FLOAT_EXPL_NWT_IT_WP_cvc5 = + ConfigNode( + "FLOAT_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 200000, + ), + checker, + ) + val config_FLOAT_EXPL_NWT_IT_WP_Z3 = + ConfigNode( + "FLOAT_EXPL_NWT_IT_WP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 200000, + ), + checker, + ) + edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_cvc5, config_FLOAT_EXPL_NWT_IT_WP_Z3, solverError)) + val config_FLOAT_EXPL_NWT_IT_WP_mathsat = + ConfigNode( + "FLOAT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + validateRefinementSolver = true, + refinement = Refinement.NWT_IT_WP, + timeoutMs = 200000, + ), + checker, + ) + edges.add( + Edge(config_FLOAT_EXPL_NWT_IT_WP_Z3, config_FLOAT_EXPL_NWT_IT_WP_mathsat, solverError) + ) + val config_FLOAT_PRED_CART_SEQ_ITP_mathsat = + ConfigNode( + "FLOAT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + validateRefinementSolver = true, + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_FLOAT_EXPL_NWT_IT_WP_cvc5, + config_FLOAT_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_FLOAT_EXPL_NWT_IT_WP_Z3, + config_FLOAT_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_FLOAT_EXPL_NWT_IT_WP_mathsat, + config_FLOAT_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_FLOAT_PRED_CART_SEQ_ITP_cvc5 = + ConfigNode( + "FLOAT_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge(config_FLOAT_PRED_CART_SEQ_ITP_mathsat, config_FLOAT_PRED_CART_SEQ_ITP_cvc5, solverError) + ) + val config_FLOAT_EXPL_SEQ_ITP_mathsat = + ConfigNode( + "FLOAT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + validateRefinementSolver = true, + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_FLOAT_PRED_CART_SEQ_ITP_mathsat, + config_FLOAT_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_FLOAT_PRED_CART_SEQ_ITP_cvc5, + config_FLOAT_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_FLOAT_EXPL_SEQ_ITP_cvc5 = + ConfigNode( + "FLOAT_EXPL_SEQ_ITP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add(Edge(config_FLOAT_EXPL_SEQ_ITP_mathsat, config_FLOAT_EXPL_SEQ_ITP_cvc5, solverError)) + val config_LIN_INT_EXPL_NWT_IT_WP_mathsat = + ConfigNode( + "LIN_INT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + val config_LIN_INT_EXPL_NWT_IT_WP_Z3 = + ConfigNode( + "LIN_INT_EXPL_NWT_IT_WP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, config_LIN_INT_EXPL_NWT_IT_WP_Z3, solverError) + ) + val config_LIN_INT_EXPL_SEQ_ITP_Z3 = + ConfigNode( + "LIN_INT_EXPL_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 300000, + ), + checker, + ) + edges.add( + Edge( + config_LIN_INT_EXPL_NWT_IT_WP_mathsat, + config_LIN_INT_EXPL_SEQ_ITP_Z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_LIN_INT_EXPL_NWT_IT_WP_Z3, + config_LIN_INT_EXPL_SEQ_ITP_Z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_LIN_INT_EXPL_SEQ_ITP_mathsat = + ConfigNode( + "LIN_INT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 300000, + ), + checker, + ) + edges.add( + Edge(config_LIN_INT_EXPL_SEQ_ITP_Z3, config_LIN_INT_EXPL_SEQ_ITP_mathsat, solverError) + ) + val config_LIN_INT_PRED_CART_SEQ_ITP_Z3 = + ConfigNode( + "LIN_INT_PRED_CART_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_LIN_INT_EXPL_SEQ_ITP_Z3, + config_LIN_INT_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_LIN_INT_EXPL_SEQ_ITP_mathsat, + config_LIN_INT_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_LIN_INT_PRED_CART_SEQ_ITP_mathsat = + ConfigNode( + "LIN_INT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_LIN_INT_PRED_CART_SEQ_ITP_Z3, + config_LIN_INT_PRED_CART_SEQ_ITP_mathsat, + solverError, + ) + ) + val config_LIN_INT_PRED_CART_SEQ_ITP_z3 = + ConfigNode( + "LIN_INT_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "z3:4.12.2", + refinementSolver = "z3:4.12.2", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_LIN_INT_PRED_CART_SEQ_ITP_mathsat, + config_LIN_INT_PRED_CART_SEQ_ITP_z3, + solverError, + ) + ) + val config_NONLIN_INT_EXPL_NWT_IT_WP_Z3 = + ConfigNode( + "NONLIN_INT_EXPL_NWT_IT_WP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + val config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat = + ConfigNode( + "NONLIN_INT_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, + config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat, + solverError, + ) + ) + val config_NONLIN_INT_EXPL_SEQ_ITP_Z3 = + ConfigNode( + "NONLIN_INT_EXPL_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, + config_NONLIN_INT_EXPL_SEQ_ITP_Z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_NWT_IT_WP_mathsat, + config_NONLIN_INT_EXPL_SEQ_ITP_Z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_NONLIN_INT_EXPL_SEQ_ITP_z3 = + ConfigNode( + "NONLIN_INT_EXPL_SEQ_ITP_z3:4.12.2-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "z3:4.12.2", + refinementSolver = "z3:4.12.2", + refinement = Refinement.SEQ_ITP, + timeoutMs = 100000, + ), + checker, + ) + edges.add( + Edge(config_NONLIN_INT_EXPL_SEQ_ITP_Z3, config_NONLIN_INT_EXPL_SEQ_ITP_z3, solverError) + ) + val config_NONLIN_INT_EXPL_SEQ_ITP_mathsat = + ConfigNode( + "NONLIN_INT_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 200000, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_SEQ_ITP_Z3, + config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_SEQ_ITP_z3, + config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat = + ConfigNode( + "NONLIN_INT_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_EXPL_SEQ_ITP_mathsat, + config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3 = + ConfigNode( + "NONLIN_INT_PRED_CART_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, + config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3, + solverError, + ) + ) + val config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5 = + ConfigNode( + "NONLIN_INT_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_NONLIN_INT_PRED_CART_SEQ_ITP_mathsat, + config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_NONLIN_INT_PRED_CART_SEQ_ITP_Z3, + config_NONLIN_INT_EXPL_NWT_IT_WP_cvc5, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_ARR_EXPL_NWT_IT_WP_cvc5 = + ConfigNode( + "ARR_EXPL_NWT_IT_WP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + val config_ARR_EXPL_NWT_IT_WP_Z3 = + ConfigNode( + "ARR_EXPL_NWT_IT_WP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 100000, + ), + checker, + ) + edges.add(Edge(config_ARR_EXPL_NWT_IT_WP_cvc5, config_ARR_EXPL_NWT_IT_WP_Z3, solverError)) + val config_ARR_PRED_CART_SEQ_ITP_Z3 = + ConfigNode( + "ARR_PRED_CART_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 300000, + ), + checker, + ) + edges.add( + Edge( + config_ARR_EXPL_NWT_IT_WP_cvc5, + config_ARR_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_ARR_EXPL_NWT_IT_WP_Z3, + config_ARR_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_ARR_PRED_CART_SEQ_ITP_z3 = + ConfigNode( + "ARR_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "z3:4.12.2", + refinementSolver = "z3:4.12.2", + refinement = Refinement.SEQ_ITP, + timeoutMs = 300000, + ), + checker, + ) + edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_Z3, config_ARR_PRED_CART_SEQ_ITP_z3, solverError)) + val config_ARR_PRED_CART_SEQ_ITP_princess = + ConfigNode( + "ARR_PRED_CART_SEQ_ITP_princess:2023-06-19-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "princess:2023-06-19", + refinementSolver = "princess:2023-06-19", + refinement = Refinement.SEQ_ITP, + timeoutMs = 500000, + ), + checker, + ) + edges.add( + Edge( + config_ARR_PRED_CART_SEQ_ITP_Z3, + config_ARR_PRED_CART_SEQ_ITP_princess, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_ARR_PRED_CART_SEQ_ITP_z3, + config_ARR_PRED_CART_SEQ_ITP_princess, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_ARR_PRED_CART_SEQ_ITP_cvc5 = + ConfigNode( + "ARR_PRED_CART_SEQ_ITP_cvc5:1.0.8-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "cvc5:1.0.8", + refinementSolver = "cvc5:1.0.8", + refinement = Refinement.SEQ_ITP, + timeoutMs = 500000, + ), + checker, + ) + edges.add( + Edge(config_ARR_PRED_CART_SEQ_ITP_princess, config_ARR_PRED_CART_SEQ_ITP_cvc5, solverError) + ) + val config_MULTITHREAD_EXPL_SEQ_ITP_Z3 = + ConfigNode( + "MULTITHREAD_EXPL_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 150000, + ), + checker, + ) + val config_MULTITHREAD_EXPL_SEQ_ITP_mathsat = + ConfigNode( + "MULTITHREAD_EXPL_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 150000, + ), + checker, + ) + edges.add( + Edge(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, config_MULTITHREAD_EXPL_SEQ_ITP_mathsat, solverError) + ) + val config_MULTITHREAD_EXPL_NWT_IT_WP_z3 = + ConfigNode( + "MULTITHREAD_EXPL_NWT_IT_WP_z3:4.12.2-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "z3:4.12.2", + refinementSolver = "z3:4.12.2", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 300000, + ), + checker, + ) + edges.add( + Edge( + config_MULTITHREAD_EXPL_SEQ_ITP_Z3, + config_MULTITHREAD_EXPL_NWT_IT_WP_z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_MULTITHREAD_EXPL_SEQ_ITP_mathsat, + config_MULTITHREAD_EXPL_NWT_IT_WP_z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat = + ConfigNode( + "MULTITHREAD_EXPL_NWT_IT_WP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.EXPL, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.NWT_IT_WP, + timeoutMs = 300000, + ), + checker, + ) + edges.add( + Edge( + config_MULTITHREAD_EXPL_NWT_IT_WP_z3, + config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat, + solverError, + ) + ) + val config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3 = + ConfigNode( + "MULTITHREAD_PRED_CART_SEQ_ITP_Z3-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "Z3", + refinementSolver = "Z3", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_MULTITHREAD_EXPL_NWT_IT_WP_z3, + config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrNotSolvableError else anyError, + ) + ) + edges.add( + Edge( + config_MULTITHREAD_EXPL_NWT_IT_WP_mathsat, + config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, + if (inProcess) timeoutOrSolverError else anyError, + ) + ) + val config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat = + ConfigNode( + "MULTITHREAD_PRED_CART_SEQ_ITP_mathsat:5.6.10-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "mathsat:5.6.10", + refinementSolver = "mathsat:5.6.10", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, + config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat, + solverError, + ) + ) + val config_MULTITHREAD_PRED_CART_SEQ_ITP_z3 = + ConfigNode( + "MULTITHREAD_PRED_CART_SEQ_ITP_z3:4.12.2-$inProcess", + baseConfig.adaptConfig( + inProcess = inProcess, + domain = Domain.PRED_CART, + abstractionSolver = "z3:4.12.2", + refinementSolver = "z3:4.12.2", + refinement = Refinement.SEQ_ITP, + timeoutMs = 0, + ), + checker, + ) + edges.add( + Edge( + config_MULTITHREAD_PRED_CART_SEQ_ITP_mathsat, + config_MULTITHREAD_PRED_CART_SEQ_ITP_z3, + solverError, + ) + ) + if (trait == ArithmeticTrait.BITWISE) { + return STM(config_BITWISE_EXPL_NWT_IT_WP_cvc5, edges) + } - if (trait == ArithmeticTrait.FLOAT) { - return STM(config_FLOAT_EXPL_NWT_IT_WP_cvc5, edges) - } + if (trait == ArithmeticTrait.FLOAT) { + return STM(config_FLOAT_EXPL_NWT_IT_WP_cvc5, edges) + } - if (trait == ArithmeticTrait.LIN_INT) { - return STM(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, edges) - } + if (trait == ArithmeticTrait.LIN_INT) { + return STM(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, edges) + } - if (trait == ArithmeticTrait.NONLIN_INT) { - return STM(config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, edges) - } + if (trait == ArithmeticTrait.NONLIN_INT) { + return STM(config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, edges) + } - if (trait == ArithmeticTrait.ARR) { - return STM(config_ARR_EXPL_NWT_IT_WP_cvc5, edges) - } + if (trait == ArithmeticTrait.ARR) { + return STM(config_ARR_EXPL_NWT_IT_WP_cvc5, edges) + } - if (trait == ArithmeticTrait.MULTITHREAD) { - return STM(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, edges) - } + if (trait == ArithmeticTrait.MULTITHREAD) { + return STM(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, edges) + } + error("Unknown trait!") + } - error("Unknown trait!") + val mainTrait = + when { + parseContext.multiThreading -> ArithmeticTrait.MULTITHREAD + ArithmeticTrait.FLOAT in parseContext.arithmeticTraits -> ArithmeticTrait.FLOAT + ArithmeticTrait.ARR in parseContext.arithmeticTraits -> ArithmeticTrait.ARR + ArithmeticTrait.BITWISE in parseContext.arithmeticTraits -> ArithmeticTrait.BITWISE + ArithmeticTrait.NONLIN_INT in parseContext.arithmeticTraits -> ArithmeticTrait.NONLIN_INT + else -> ArithmeticTrait.LIN_INT } - val mainTrait = - when { - parseContext.multiThreading -> ArithmeticTrait.MULTITHREAD - ArithmeticTrait.FLOAT in parseContext.arithmeticTraits -> ArithmeticTrait.FLOAT - ArithmeticTrait.ARR in parseContext.arithmeticTraits -> ArithmeticTrait.ARR - ArithmeticTrait.BITWISE in parseContext.arithmeticTraits -> ArithmeticTrait.BITWISE - ArithmeticTrait.NONLIN_INT in parseContext.arithmeticTraits -> ArithmeticTrait.NONLIN_INT - else -> ArithmeticTrait.LIN_INT - } - - logger.write(Logger.Level.RESULT, "Using portfolio $mainTrait\n") + logger.write(Logger.Level.RESULT, "Using portfolio $mainTrait\n") - val inProcess = HierarchicalNode("InProcess", getStm(mainTrait, true)) - val notInProcess = HierarchicalNode("NotInprocess", getStm(mainTrait, false)) + val inProcess = HierarchicalNode("InProcess", getStm(mainTrait, true)) + val notInProcess = HierarchicalNode("NotInprocess", getStm(mainTrait, false)) - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - return if (portfolioConfig.debugConfig.debug) getStm(mainTrait, false) else STM(inProcess, setOf(fallbackEdge)) -} \ No newline at end of file + return if (portfolioConfig.debugConfig.debug) getStm(mainTrait, false) + else STM(inProcess, setOf(fallbackEdge)) +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/horn.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/horn.kt index 5a3933b59c..a1a7d9a6d2 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/horn.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/horn.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.portfolio import hu.bme.mit.theta.common.logging.Logger @@ -29,121 +28,134 @@ import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass import java.nio.file.Paths fun hornPortfolio( - xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger): STM { - - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } - - var baseConfig = XcfaConfig( - inputConfig = InputConfig( - input = null, - xcfaWCtx = Triple(xcfa, mcm, parseContext), - propertyFile = null, - property = portfolioConfig.inputConfig.property), - frontendConfig = FrontendConfig( - lbeLevel = LbePass.level, - loopUnroll = LoopUnrollPass.UNROLL_LIMIT, - inputType = InputType.C, - specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient)), - backendConfig = BackendConfig( - backend = Backend.CHC, - solverHome = portfolioConfig.backendConfig.solverHome, - timeoutMs = 0, - specConfig = HornConfig( - solver = "z3:4.13.0", - validateSolver = false - )), - outputConfig = OutputConfig( - versionInfo = false, - resultFolder = Paths.get("./").toFile(), // cwd - cOutputConfig = COutputConfig(disable = true), - witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true), - enableOutput = portfolioConfig.outputConfig.enableOutput, + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): STM { + + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } + + var baseConfig = + XcfaConfig( + inputConfig = + InputConfig( + input = null, + xcfaWCtx = Triple(xcfa, mcm, parseContext), + propertyFile = null, + property = portfolioConfig.inputConfig.property, + ), + frontendConfig = + FrontendConfig( + lbeLevel = LbePass.level, + loopUnroll = LoopUnrollPass.UNROLL_LIMIT, + inputType = InputType.C, + specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient), + ), + backendConfig = + BackendConfig( + backend = Backend.CHC, + solverHome = portfolioConfig.backendConfig.solverHome, + timeoutMs = 0, + specConfig = HornConfig(solver = "z3:4.13.0", validateSolver = false), ), - debugConfig = portfolioConfig.debugConfig + outputConfig = + OutputConfig( + versionInfo = false, + resultFolder = Paths.get("./").toFile(), // cwd + cOutputConfig = COutputConfig(disable = true), + witnessConfig = + WitnessConfig( + disable = false, + concretizerSolver = "Z3", + validateConcretizerSolver = false, + ), + argConfig = ArgConfig(disable = true), + enableOutput = portfolioConfig.outputConfig.enableOutput, + ), + debugConfig = portfolioConfig.debugConfig, ) - if (parseContext.multiThreading) { - throw UnsupportedOperationException("Multithreading for horn checkers not supported") - } + if (parseContext.multiThreading) { + throw UnsupportedOperationException("Multithreading for horn checkers not supported") + } - if (!xcfa.isInlined) { - throw UnsupportedOperationException("Recursive XCFA for horn checkers not supported") - } + if (!xcfa.isInlined) { + throw UnsupportedOperationException("Recursive XCFA for horn checkers not supported") + } - val timeoutOrNotSolvableError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - ErrorCodeException(ExitCodes.SERVER_ERROR.code), + val timeoutOrNotSolvableError = + ExceptionTrigger( + fallthroughExceptions = + setOf( + ErrorCodeException(ExitCodes.SOLVER_ERROR.code), + ErrorCodeException(ExitCodes.SERVER_ERROR.code), ), - label = "TimeoutOrNotSolvableError" + label = "TimeoutOrNotSolvableError", ) - val timeoutOrSolverError = ExceptionTrigger( - fallthroughExceptions = setOf( - ErrorCodeException(ExitCodes.SERVER_ERROR.code), - ), - label = "TimeoutOrSolverError" + val timeoutOrSolverError = + ExceptionTrigger( + fallthroughExceptions = setOf(ErrorCodeException(ExitCodes.SERVER_ERROR.code)), + label = "TimeoutOrSolverError", ) - val solverError = ExceptionTrigger( - ErrorCodeException(ExitCodes.SOLVER_ERROR.code), - label = "SolverError" + val solverError = + ExceptionTrigger(ErrorCodeException(ExitCodes.SOLVER_ERROR.code), label = "SolverError") + + val anyError = ExceptionTrigger(label = "Anything") + + fun XcfaConfig<*, HornConfig>.adaptConfig( + solver: String = "z3:4.13.0", + timeoutMs: Long = 0, + inProcess: Boolean = this.backendConfig.inProcess, + ): XcfaConfig<*, HornConfig> { + return copy( + backendConfig = + backendConfig.copy( + timeoutMs = timeoutMs, + inProcess = inProcess, + specConfig = backendConfig.specConfig!!.copy(solver = solver), + ) ) - - val anyError = ExceptionTrigger(label = "Anything") - - fun XcfaConfig<*, HornConfig>.adaptConfig( - solver: String = "z3:4.13.0", - timeoutMs: Long = 0, - inProcess: Boolean = this.backendConfig.inProcess - ): XcfaConfig<*, HornConfig> { - return copy(backendConfig = backendConfig.copy( - timeoutMs = timeoutMs, - inProcess = inProcess, - specConfig = backendConfig.specConfig!!.copy( - solver = solver, - ) - )) - } - - fun getStm(inProcess: Boolean): STM { - val edges = LinkedHashSet() - val configZ3 = ConfigNode("Z3-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - timeoutMs = 100_000 - ), checker) - val configEldarica = ConfigNode("Eldarica-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - solver = "eldarica:2.1", - timeoutMs = 500_000 - ), checker) - val configGolem = ConfigNode("Golem-$inProcess", - baseConfig.adaptConfig( - inProcess = inProcess, - solver = "golem:0.5.0", - timeoutMs = 300_000 - ), checker) - - edges.add(Edge(configZ3, configEldarica, anyError)) - edges.add(Edge(configEldarica, configGolem, anyError)) - - return STM(configZ3, edges) - } - - logger.write(Logger.Level.RESULT, "Using CHC portfolio\n") - - val inProcess = HierarchicalNode("InProcess", getStm(true)) - val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) - - val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) - - return if (portfolioConfig.debugConfig.debug) getStm(false) else STM(inProcess, setOf(fallbackEdge)) -} \ No newline at end of file + } + + fun getStm(inProcess: Boolean): STM { + val edges = LinkedHashSet() + val configZ3 = + ConfigNode( + "Z3-$inProcess", + baseConfig.adaptConfig(inProcess = inProcess, timeoutMs = 100_000), + checker, + ) + val configEldarica = + ConfigNode( + "Eldarica-$inProcess", + baseConfig.adaptConfig(inProcess = inProcess, solver = "eldarica:2.1", timeoutMs = 500_000), + checker, + ) + val configGolem = + ConfigNode( + "Golem-$inProcess", + baseConfig.adaptConfig(inProcess = inProcess, solver = "golem:0.5.0", timeoutMs = 300_000), + checker, + ) + + edges.add(Edge(configZ3, configEldarica, anyError)) + edges.add(Edge(configEldarica, configGolem, anyError)) + + return STM(configZ3, edges) + } + + logger.write(Logger.Level.RESULT, "Using CHC portfolio\n") + + val inProcess = HierarchicalNode("InProcess", getStm(true)) + val notInProcess = HierarchicalNode("NotInprocess", getStm(false)) + + val fallbackEdge = Edge(inProcess, notInProcess, ExceptionTrigger(label = "Anything")) + + return if (portfolioConfig.debugConfig.debug) getStm(false) + else STM(inProcess, setOf(fallbackEdge)) +} diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/stm.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/stm.kt index d98b44e8f9..a68572f6a2 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/stm.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/stm.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.portfolio import hu.bme.mit.theta.analysis.algorithm.SafetyResult @@ -21,113 +20,137 @@ import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig abstract class Node(val name: String) { - val outEdges: MutableSet = LinkedHashSet() - var parent: STM? = null + val outEdges: MutableSet = LinkedHashSet() + var parent: STM? = null + + abstract fun execute(): Pair - abstract fun execute(): Pair - abstract fun visualize(): String + abstract fun visualize(): String } class HierarchicalNode(name: String, private val innerSTM: STM) : Node(name) { - override fun execute(): Pair = innerSTM.execute() - override fun visualize(): String = """state $name { + override fun execute(): Pair = innerSTM.execute() + + override fun visualize(): String = + """state $name { ${innerSTM.visualize()} -}""".trimIndent() +}""" + .trimIndent() } -class ConfigNode(name: String, private val config: XcfaConfig<*, *>, - private val check: (config: XcfaConfig<*, *>) -> SafetyResult<*, *>) : Node(name) { - - override fun execute(): Pair { - println("Current configuration: $config") - return Pair(config, check(config)) - } - - override fun visualize(): String = config.toString().lines() // TODO: reintroduce visualize() - .map { "state ${name.replace(Regex("[:\\.-]+"), "_")}: $it" }.reduce { a, b -> "$a\n$b" } +class ConfigNode( + name: String, + private val config: XcfaConfig<*, *>, + private val check: (config: XcfaConfig<*, *>) -> SafetyResult<*, *>, +) : Node(name) { + + override fun execute(): Pair { + println("Current configuration: $config") + return Pair(config, check(config)) + } + + override fun visualize(): String = + config + .toString() + .lines() // TODO: reintroduce visualize() + .map { "state ${name.replace(Regex("[:\\.-]+"), "_")}: $it" } + .reduce { a, b -> "$a\n$b" } } -data class Edge(val source: Node, - val target: Node, - val trigger: (Throwable) -> Boolean, - val guard: (Node, Edge) -> Boolean = { _, _ -> true }) { +data class Edge( + val source: Node, + val target: Node, + val trigger: (Throwable) -> Boolean, + val guard: (Node, Edge) -> Boolean = { _, _ -> true }, +) { - init { - source.outEdges.add(this) - } + init { + source.outEdges.add(this) + } - fun visualize(): String = """${source.name.replace(Regex("[:\\.-]+"), "_")} --> ${ + fun visualize(): String = + """${source.name.replace(Regex("[:\\.-]+"), "_")} --> ${ target.name.replace(Regex("[:\\.-]+"), "_") } : $trigger """ - } // if the exceptions set is empty, it catches all exceptions class ExceptionTrigger( - val exceptions: Set = emptySet(), - val fallthroughExceptions: Set = emptySet(), - val label: String? = null + val exceptions: Set = emptySet(), + val fallthroughExceptions: Set = emptySet(), + val label: String? = null, ) : (Throwable) -> Boolean { - constructor(vararg exceptions: Throwable, label: String? = null) : this(exceptions.toSet(), - label = label) + constructor( + vararg exceptions: Throwable, + label: String? = null, + ) : this(exceptions.toSet(), label = label) - override fun invoke(e: Throwable): Boolean = - if (exceptions.isNotEmpty()) - exceptions.contains(e) && !fallthroughExceptions.contains(e) - else - !fallthroughExceptions.contains(e) + override fun invoke(e: Throwable): Boolean = + if (exceptions.isNotEmpty()) exceptions.contains(e) && !fallthroughExceptions.contains(e) + else !fallthroughExceptions.contains(e) - override fun toString(): String = - label ?: ((if (exceptions.isNotEmpty()) exceptions.toString() else "*") + - (if (fallthroughExceptions.isNotEmpty()) ", not $fallthroughExceptions" else "")) + override fun toString(): String = + label + ?: ((if (exceptions.isNotEmpty()) exceptions.toString() else "*") + + (if (fallthroughExceptions.isNotEmpty()) ", not $fallthroughExceptions" else "")) } data class STM(val initNode: Node, val edges: Set) { - init { - val nodes = edges.map { listOf(it.source, it.target) }.flatten().toSet() - nodes.forEach { - check( - it.parent == null || it.parent === this) { "Edges to behave encapsulated (offender: $it)" } - it.parent = this - } + init { + val nodes = edges.map { listOf(it.source, it.target) }.flatten().toSet() + nodes.forEach { + check(it.parent == null || it.parent === this) { + "Edges to behave encapsulated (offender: $it)" + } + it.parent = this } - - private fun visualizeNodes(): String = edges.map { listOf(it.source, it.target) }.flatten() - .toSet().map { it.visualize() }.reduce { a, b -> "$a\n$b" } - - fun visualize(): String = """ + } + + private fun visualizeNodes(): String = + edges + .map { listOf(it.source, it.target) } + .flatten() + .toSet() + .map { it.visualize() } + .reduce { a, b -> "$a\n$b" } + + fun visualize(): String = + """ ${visualizeNodes()} [*] --> ${initNode.name.replace(Regex("[:\\.-]+"), "_")} ${edges.map { it.visualize() }.reduce { a, b -> "$a\n$b" }} -""".trimMargin() - - fun execute(): Pair { - var currentNode: Node = initNode - while (true) { - try { - return currentNode.execute() - } catch (e: Throwable) { - println("Caught exception: $e") - val edge: Edge? = currentNode.outEdges.find { it.trigger(e) } - if (edge != null) { - println("Handling exception as ${edge.trigger}") - currentNode = edge.target - } else { - println("Could not handle trigger $e (Available triggers: ${ +""" + .trimMargin() + + fun execute(): Pair { + var currentNode: Node = initNode + while (true) { + try { + return currentNode.execute() + } catch (e: Throwable) { + println("Caught exception: $e") + val edge: Edge? = currentNode.outEdges.find { it.trigger(e) } + if (edge != null) { + println("Handling exception as ${edge.trigger}") + currentNode = edge.target + } else { + println( + "Could not handle trigger $e (Available triggers: ${ currentNode.outEdges.map { it.trigger }.toList() - })") - throw e - } - } + })" + ) + throw e } + } } + } } -//fun XcfaConfig<*, CegarConfig>.visualize(inProcess: Boolean): String = +// fun XcfaConfig<*, CegarConfig>.visualize(inProcess: Boolean): String = // """solvers: $abstractionSolver, $refinementSolver // |domain: $domain, search: $search, initprec: $initPrec, por: $porLevel // |refinement: $refinement, pruneStrategy: $pruneStrategy -// |timeout: $timeoutMs ms, inProcess: $inProcess""".trimMargin() \ No newline at end of file +// |timeout: $timeoutMs ms, inProcess: $inProcess""".trimMargin() diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/PropertyUtils.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/PropertyUtils.kt index c053280a14..967b38ed04 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/PropertyUtils.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/PropertyUtils.kt @@ -13,32 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.utils import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.xcfa.analysis.ErrorDetection import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig -fun determineProperty(config: XcfaConfig<*, *>, logger: Logger): ErrorDetection = config.inputConfig.propertyFile?.run { +fun determineProperty(config: XcfaConfig<*, *>, logger: Logger): ErrorDetection = + config.inputConfig.propertyFile?.run { val propertyFile = config.inputConfig.propertyFile!! when { - propertyFile.name.endsWith("unreach-call.prp") -> { - ErrorDetection.ERROR_LOCATION - } + propertyFile.name.endsWith("unreach-call.prp") -> { + ErrorDetection.ERROR_LOCATION + } - propertyFile.name.endsWith("no-data-race.prp") -> { - ErrorDetection.DATA_RACE - } + propertyFile.name.endsWith("no-data-race.prp") -> { + ErrorDetection.DATA_RACE + } - propertyFile.name.endsWith("no-overflow.prp") -> { - ErrorDetection.OVERFLOW - } + propertyFile.name.endsWith("no-overflow.prp") -> { + ErrorDetection.OVERFLOW + } - else -> { - logger.write(Logger.Level.INFO, - "Unknown property $propertyFile, using full state space exploration (no refinement)\n") - ErrorDetection.NO_ERROR - } + else -> { + logger.write( + Logger.Level.INFO, + "Unknown property $propertyFile, using full state space exploration (no refinement)\n", + ) + ErrorDetection.NO_ERROR + } } -} ?: config.inputConfig.property \ No newline at end of file + } ?: config.inputConfig.property diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaParser.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaParser.kt index cb18d5c237..a14da06c84 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaParser.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaParser.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.cli.utils import hu.bme.mit.theta.c2xcfa.getXcfaFromC @@ -32,92 +31,142 @@ import hu.bme.mit.theta.xcfa.cli.params.InputType import hu.bme.mit.theta.xcfa.cli.params.XcfaConfig import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.passes.ChcPasses -import org.antlr.v4.runtime.CharStreams import java.io.File import java.io.FileInputStream import java.io.FileReader import javax.script.ScriptEngine import javax.script.ScriptEngineManager import kotlin.system.exitProcess +import org.antlr.v4.runtime.CharStreams +fun getXcfa( + config: XcfaConfig<*, *>, + parseContext: ParseContext, + logger: Logger, + uniqueWarningLogger: Logger, +) = + try { + when (config.frontendConfig.inputType) { + InputType.CHC -> { + val chcConfig = config.frontendConfig.specConfig as CHCFrontendConfig + parseChc( + config.inputConfig.input!!, + chcConfig.chcTransformation, + parseContext, + logger, + uniqueWarningLogger, + ) + } -fun getXcfa(config: XcfaConfig<*, *>, parseContext: ParseContext, logger: Logger, uniqueWarningLogger: Logger) = - try { - when (config.frontendConfig.inputType) { - InputType.CHC -> { - val chcConfig = config.frontendConfig.specConfig as CHCFrontendConfig - parseChc(config.inputConfig.input!!, chcConfig.chcTransformation, parseContext, logger, - uniqueWarningLogger) - } - - InputType.C -> { - parseC(config.inputConfig.input!!, config.inputConfig.property, parseContext, logger, - uniqueWarningLogger) - } + InputType.C -> { + parseC( + config.inputConfig.input!!, + config.inputConfig.property, + parseContext, + logger, + uniqueWarningLogger, + ) + } - InputType.LLVM -> XcfaUtils.fromFile(config.inputConfig.input!!, ArithmeticType.efficient) + InputType.LLVM -> XcfaUtils.fromFile(config.inputConfig.input!!, ArithmeticType.efficient) - InputType.LITMUS -> LitmusInterpreter.getXcfa(config.inputConfig.input!!) + InputType.LITMUS -> LitmusInterpreter.getXcfa(config.inputConfig.input!!) - InputType.JSON -> { - val gson = getGson() - gson.fromJson(config.inputConfig.input!!.readText(), XCFA::class.java) - } + InputType.JSON -> { + val gson = getGson() + gson.fromJson(config.inputConfig.input!!.readText(), XCFA::class.java) + } - InputType.DSL -> { - val kotlinEngine: ScriptEngine = ScriptEngineManager().getEngineByExtension("kts") - kotlinEngine.eval(FileReader(config.inputConfig.input!!)) as XCFA - } - } - } catch (e: Exception) { - if (config.debugConfig.stacktrace) e.printStackTrace() - val location = e.stackTrace.filter { it.className.startsWith("hu.bme.mit.theta") }.first().toString() - logger.write(Logger.Level.RESULT, "Frontend failed! ($location, $e)\n") - exitProcess(ExitCodes.FRONTEND_FAILED.code) + InputType.DSL -> { + val kotlinEngine: ScriptEngine = ScriptEngineManager().getEngineByExtension("kts") + kotlinEngine.eval(FileReader(config.inputConfig.input!!)) as XCFA + } } + } catch (e: Exception) { + if (config.debugConfig.stacktrace) e.printStackTrace() + val location = + e.stackTrace.filter { it.className.startsWith("hu.bme.mit.theta") }.first().toString() + logger.write(Logger.Level.RESULT, "Frontend failed! ($location, $e)\n") + exitProcess(ExitCodes.FRONTEND_FAILED.code) + } -private fun parseC(input: File, explicitProperty: ErrorDetection, parseContext: ParseContext, logger: Logger, - uniqueWarningLogger: Logger): XCFA { - val xcfaFromC = try { - val stream = FileInputStream(input) - getXcfaFromC(stream, parseContext, false, - explicitProperty == ErrorDetection.OVERFLOW, uniqueWarningLogger).first +private fun parseC( + input: File, + explicitProperty: ErrorDetection, + parseContext: ParseContext, + logger: Logger, + uniqueWarningLogger: Logger, +): XCFA { + val xcfaFromC = + try { + val stream = FileInputStream(input) + getXcfaFromC( + stream, + parseContext, + false, + explicitProperty == ErrorDetection.OVERFLOW, + uniqueWarningLogger, + ) + .first } catch (e: Throwable) { - if (parseContext.arithmetic == ArchitectureConfig.ArithmeticType.efficient) { - parseContext.arithmetic = ArchitectureConfig.ArithmeticType.bitvector - logger.write(Logger.Level.INFO, "Retrying parsing with bitvector arithmetic...\n") - val stream = FileInputStream(input) - val xcfa = getXcfaFromC(stream, parseContext, false, - explicitProperty == ErrorDetection.OVERFLOW, uniqueWarningLogger).first - parseContext.addArithmeticTrait(ArithmeticTrait.BITWISE) - xcfa - } else { - throw e - } + if (parseContext.arithmetic == ArchitectureConfig.ArithmeticType.efficient) { + parseContext.arithmetic = ArchitectureConfig.ArithmeticType.bitvector + logger.write(Logger.Level.INFO, "Retrying parsing with bitvector arithmetic...\n") + val stream = FileInputStream(input) + val xcfa = + getXcfaFromC( + stream, + parseContext, + false, + explicitProperty == ErrorDetection.OVERFLOW, + uniqueWarningLogger, + ) + .first + parseContext.addArithmeticTrait(ArithmeticTrait.BITWISE) + xcfa + } else { + throw e + } } - logger.write(Logger.Level.RESULT, - "Arithmetic: ${parseContext.arithmeticTraits}\n") - return xcfaFromC + logger.write(Logger.Level.RESULT, "Arithmetic: ${parseContext.arithmeticTraits}\n") + return xcfaFromC } -private fun parseChc(input: File, chcTransformation: ChcFrontend.ChcTransformation, parseContext: ParseContext, - logger: Logger, uniqueWarningLogger: Logger): XCFA { - var chcFrontend: ChcFrontend - val xcfaBuilder = if (chcTransformation == ChcFrontend.ChcTransformation.PORTFOLIO) { // try forward, fallback to backward - chcFrontend = ChcFrontend(ChcFrontend.ChcTransformation.FORWARD) - try { - chcFrontend.buildXcfa(CharStreams.fromStream(FileInputStream(input)), - ChcPasses(parseContext, uniqueWarningLogger)) - } catch (e: UnsupportedOperationException) { - logger.write(Logger.Level.INFO, "Non-linear CHC found, retrying using backward transformation...\n") - chcFrontend = ChcFrontend(ChcFrontend.ChcTransformation.BACKWARD) - chcFrontend.buildXcfa(CharStreams.fromStream(FileInputStream(input)), - ChcPasses(parseContext, uniqueWarningLogger)) - } +private fun parseChc( + input: File, + chcTransformation: ChcFrontend.ChcTransformation, + parseContext: ParseContext, + logger: Logger, + uniqueWarningLogger: Logger, +): XCFA { + var chcFrontend: ChcFrontend + val xcfaBuilder = + if ( + chcTransformation == ChcFrontend.ChcTransformation.PORTFOLIO + ) { // try forward, fallback to backward + chcFrontend = ChcFrontend(ChcFrontend.ChcTransformation.FORWARD) + try { + chcFrontend.buildXcfa( + CharStreams.fromStream(FileInputStream(input)), + ChcPasses(parseContext, uniqueWarningLogger), + ) + } catch (e: UnsupportedOperationException) { + logger.write( + Logger.Level.INFO, + "Non-linear CHC found, retrying using backward transformation...\n", + ) + chcFrontend = ChcFrontend(ChcFrontend.ChcTransformation.BACKWARD) + chcFrontend.buildXcfa( + CharStreams.fromStream(FileInputStream(input)), + ChcPasses(parseContext, uniqueWarningLogger), + ) + } } else { - chcFrontend = ChcFrontend(chcTransformation) - chcFrontend.buildXcfa(CharStreams.fromStream(FileInputStream(input)), - ChcPasses(parseContext, uniqueWarningLogger)) + chcFrontend = ChcFrontend(chcTransformation) + chcFrontend.buildXcfa( + CharStreams.fromStream(FileInputStream(input)), + ChcPasses(parseContext, uniqueWarningLogger), + ) } - return xcfaBuilder.build() -} \ No newline at end of file + return xcfaBuilder.build() +} diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliPortfolioTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliPortfolioTest.kt index 9d9dc0e0f2..9851e38129 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliPortfolioTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliPortfolioTest.kt @@ -27,58 +27,67 @@ import hu.bme.mit.theta.xcfa.cli.portfolio.STM import hu.bme.mit.theta.xcfa.cli.portfolio.complexPortfolio23 import hu.bme.mit.theta.xcfa.cli.portfolio.complexPortfolio24 import hu.bme.mit.theta.xcfa.model.XCFA +import java.util.stream.Stream import org.junit.jupiter.api.Assertions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream class XcfaCliPortfolioTest { - companion object { - - - @JvmStatic - fun portfolios(): Stream { - return Stream.of( - Arguments.of({ xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger -> - complexPortfolio23(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) - }), - Arguments.of({ xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger -> - complexPortfolio24(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) - }), - ) - } + companion object { + @JvmStatic + fun portfolios(): Stream { + return Stream.of( + Arguments.of({ + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger -> + complexPortfolio23(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) + }), + Arguments.of({ + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger -> + complexPortfolio24(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) + }), + ) } + } - @ParameterizedTest - @MethodSource("portfolios") - fun testPortfolio(portfolio: (xcfa: XCFA, + @ParameterizedTest + @MethodSource("portfolios") + fun testPortfolio( + portfolio: + ( + xcfa: XCFA, mcm: MCM, parseContext: ParseContext, portfolioConfig: XcfaConfig<*, *>, logger: Logger, - uniqueLogger: Logger) -> STM) { + uniqueLogger: Logger, + ) -> STM + ) { - for (value in ArithmeticTrait.values()) { + for (value in ArithmeticTrait.values()) { - val stm = portfolio(XCFA("name", setOf()), emptySet(), ParseContext(), - XcfaConfig(), NullLogger.getInstance(), NullLogger.getInstance()) - - Assertions.assertTrue(stm.visualize().isNotEmpty()) - } + val stm = + portfolio( + XCFA("name", setOf()), + emptySet(), + ParseContext(), + XcfaConfig(), + NullLogger.getInstance(), + NullLogger.getInstance(), + ) + Assertions.assertTrue(stm.visualize().isNotEmpty()) } - - + } } diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliVerifyTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliVerifyTest.kt index c5fb4dbb56..084cbdbd98 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliVerifyTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliVerifyTest.kt @@ -21,6 +21,11 @@ import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.frontend.chc.ChcFrontend import hu.bme.mit.theta.solver.smtlib.SmtLibSolverManager import hu.bme.mit.theta.xcfa.cli.XcfaCli.Companion.main +import java.nio.file.Path +import java.util.stream.Stream +import kotlin.io.path.absolutePathString +import kotlin.io.path.createTempDirectory +import kotlin.io.path.exists import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.BeforeAll @@ -28,313 +33,367 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -import java.nio.file.Path -import java.util.stream.Stream -import kotlin.io.path.absolutePathString -import kotlin.io.path.createTempDirectory -import kotlin.io.path.exists class XcfaCliVerifyTest { - companion object { - - private val SMTLIB_HOME: Path = SmtLibSolverManager.HOME - private val solvers = listOf( - "z3:4.13.0", - "eldarica:2.1", - "golem:0.5.0", - "mathsat:5.6.10", - "cvc5:1.0.8" - ) - - private fun installSolver(name: String) { - try { - SmtLibSolverManager.create(SMTLIB_HOME, ConsoleLogger(Logger.Level.DETAIL)) - .use { solverManager -> - val solverVersion = SmtLibSolverManager.getSolverVersion(name) - val solverName = SmtLibSolverManager.getSolverName(name) - if (solverManager.managesSolver(name) && !solverManager.getInstalledVersions(solverName) - .contains(solverManager.getVersionString(solverName, solverVersion, false)) - ) { - solverManager.install(solverName, solverVersion, solverVersion, null, false) - } - } - } catch (e: Exception) { - e.printStackTrace() // best effort - } - } + companion object { - @BeforeAll - @JvmStatic - fun installSolvers() { - if (OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { - solvers.forEach(this::installSolver) - } - } - - @JvmStatic - fun cFiles(): Stream { - return Stream.of( - Arguments.of("/c/dekker.i", "--search DFS --por-level SPOR"), - Arguments.of("/c/litmustest/singlethread/00assignment.c", null), - Arguments.of("/c/litmustest/singlethread/01cast.c", null), - Arguments.of("/c/litmustest/singlethread/02types.c", null), - Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), - Arguments.of("/c/litmustest/singlethread/04real.c", null), - Arguments.of("/c/litmustest/singlethread/06arrays.c", null), - Arguments.of("/c/litmustest/singlethread/07arrayinit.c", null), - Arguments.of("/c/litmustest/singlethread/08vararray.c", null), - Arguments.of("/c/litmustest/singlethread/13typedef.c", "--domain PRED_CART"), - Arguments.of("/c/litmustest/singlethread/14ushort.c", null), - Arguments.of("/c/litmustest/singlethread/15addition.c", null), - Arguments.of("/c/litmustest/singlethread/16loop.c", null), - Arguments.of("/c/litmustest/singlethread/17recursive.c", null), - Arguments.of("/c/litmustest/singlethread/18multithread.c", "--search DFS --por-level SPOR"), - Arguments.of("/c/litmustest/singlethread/19dportest.c", "--search DFS --por-level SPOR"), - Arguments.of("/c/litmustest/singlethread/20testinline.c", null), - Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), - Arguments.of("/c/litmustest/singlethread/22nondet.c", null), - Arguments.of("/c/litmustest/singlethread/23overflow.c", "--domain PRED_CART"), - ) - } + private val SMTLIB_HOME: Path = SmtLibSolverManager.HOME + private val solvers = + listOf("z3:4.13.0", "eldarica:2.1", "golem:0.5.0", "mathsat:5.6.10", "cvc5:1.0.8") - @JvmStatic - fun singleThreadedCFiles(): Stream { - return Stream.of( - Arguments.of("/c/litmustest/singlethread/00assignment.c", null), - Arguments.of("/c/litmustest/singlethread/01cast.c", null), - Arguments.of("/c/litmustest/singlethread/02types.c", null), - Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), - Arguments.of("/c/litmustest/singlethread/04real.c", null), - Arguments.of("/c/litmustest/singlethread/13typedef.c", "--domain PRED_CART"), - Arguments.of("/c/litmustest/singlethread/14ushort.c", null), - Arguments.of("/c/litmustest/singlethread/15addition.c", null), - Arguments.of("/c/litmustest/singlethread/20testinline.c", null), - Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), - Arguments.of("/c/litmustest/singlethread/22nondet.c", null), - Arguments.of("/c/litmustest/singlethread/23overflow.c", "--domain PRED_CART"), - ) - } - - @JvmStatic - fun cFilesShort(): Stream { - return Stream.of( - Arguments.of("/c/dekker.i", "--search DFS --por-level SPOR"), - Arguments.of("/c/litmustest/singlethread/00assignment.c", null), - Arguments.of("/c/litmustest/singlethread/01cast.c", null), - Arguments.of("/c/litmustest/singlethread/02types.c", null), - Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), - Arguments.of("/c/litmustest/singlethread/04real.c", null), - ) - } - - @JvmStatic - fun cFilesShortInt(): Stream { - return Stream.of( - Arguments.of("/c/litmustest/singlethread/00assignment.c", null), - Arguments.of("/c/litmustest/singlethread/01cast.c", null), - Arguments.of("/c/litmustest/singlethread/02types.c", null), - Arguments.of("/c/litmustest/singlethread/15addition.c", null), - Arguments.of("/c/litmustest/singlethread/20testinline.c", null), - Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), - Arguments.of("/c/litmustest/singlethread/22nondet.c", null), - ) - } - - @JvmStatic - fun chcFiles(): Stream { - return Stream.of( - Arguments.of("/chc/chc-LIA-Lin_000.smt2", ChcFrontend.ChcTransformation.FORWARD, "--domain PRED_CART"), - Arguments.of("/chc/chc-LIA-Arrays_000.smt2", ChcFrontend.ChcTransformation.BACKWARD, - "--domain PRED_CART --search BFS"), - ) + private fun installSolver(name: String) { + try { + SmtLibSolverManager.create(SMTLIB_HOME, ConsoleLogger(Logger.Level.DETAIL)).use { + solverManager -> + val solverVersion = SmtLibSolverManager.getSolverVersion(name) + val solverName = SmtLibSolverManager.getSolverName(name) + if ( + solverManager.managesSolver(name) && + !solverManager + .getInstalledVersions(solverName) + .contains(solverManager.getVersionString(solverName, solverVersion, false)) + ) { + solverManager.install(solverName, solverVersion, solverVersion, null, false) + } } + } catch (e: Exception) { + e.printStackTrace() // best effort + } } - @ParameterizedTest - @MethodSource("cFiles") - fun testCVerifyDirect(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), - "--debug" - ) - main(params) + @BeforeAll + @JvmStatic + fun installSolvers() { + if (OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) { + solvers.forEach(this::installSolver) + } } - @ParameterizedTest - @MethodSource("cFilesShort") - fun testCVerifyServer(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), - "--debug" - ) - try { - main(params) - } catch (e: IllegalStateException) { - if (!e.message.equals("Done debugging")) { - throw e - } - } + @JvmStatic + fun cFiles(): Stream { + return Stream.of( + Arguments.of("/c/dekker.i", "--search DFS --por-level SPOR"), + Arguments.of("/c/litmustest/singlethread/00assignment.c", null), + Arguments.of("/c/litmustest/singlethread/01cast.c", null), + Arguments.of("/c/litmustest/singlethread/02types.c", null), + Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), + Arguments.of("/c/litmustest/singlethread/04real.c", null), + Arguments.of("/c/litmustest/singlethread/06arrays.c", null), + Arguments.of("/c/litmustest/singlethread/07arrayinit.c", null), + Arguments.of("/c/litmustest/singlethread/08vararray.c", null), + Arguments.of("/c/litmustest/singlethread/13typedef.c", "--domain PRED_CART"), + Arguments.of("/c/litmustest/singlethread/14ushort.c", null), + Arguments.of("/c/litmustest/singlethread/15addition.c", null), + Arguments.of("/c/litmustest/singlethread/16loop.c", null), + Arguments.of("/c/litmustest/singlethread/17recursive.c", null), + Arguments.of("/c/litmustest/singlethread/18multithread.c", "--search DFS --por-level SPOR"), + Arguments.of("/c/litmustest/singlethread/19dportest.c", "--search DFS --por-level SPOR"), + Arguments.of("/c/litmustest/singlethread/20testinline.c", null), + Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), + Arguments.of("/c/litmustest/singlethread/22nondet.c", null), + Arguments.of("/c/litmustest/singlethread/23overflow.c", "--domain PRED_CART"), + ) } - @ParameterizedTest - @MethodSource("cFilesShort") - fun testCVerifyPortfolio(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--input-type", "C", - "--backend", "PORTFOLIO", - "--portfolio", javaClass.getResource("/simple.kts")!!.path, - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug", - ) - try { - main(params) - } catch (e: Throwable) { - if (!e.toString().contains("Done debugging")) { - throw e - } - } + @JvmStatic + fun singleThreadedCFiles(): Stream { + return Stream.of( + Arguments.of("/c/litmustest/singlethread/00assignment.c", null), + Arguments.of("/c/litmustest/singlethread/01cast.c", null), + Arguments.of("/c/litmustest/singlethread/02types.c", null), + Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), + Arguments.of("/c/litmustest/singlethread/04real.c", null), + Arguments.of("/c/litmustest/singlethread/13typedef.c", "--domain PRED_CART"), + Arguments.of("/c/litmustest/singlethread/14ushort.c", null), + Arguments.of("/c/litmustest/singlethread/15addition.c", null), + Arguments.of("/c/litmustest/singlethread/20testinline.c", null), + Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), + Arguments.of("/c/litmustest/singlethread/22nondet.c", null), + Arguments.of("/c/litmustest/singlethread/23overflow.c", "--domain PRED_CART"), + ) } - @Test - fun testCVerifyBuiltInPortfolio() { - val params = arrayOf( - "--input-type", "C", - "--backend", "PORTFOLIO", - "--portfolio", "COMPLEX", - "--input", javaClass.getResource("/c/dekker.i")!!.path, - "--stacktrace", - "--debug", - ) - try { - main(params) - } catch (e: Throwable) { - if (!e.toString().contains("Done debugging")) { - throw e - } - } + @JvmStatic + fun cFilesShort(): Stream { + return Stream.of( + Arguments.of("/c/dekker.i", "--search DFS --por-level SPOR"), + Arguments.of("/c/litmustest/singlethread/00assignment.c", null), + Arguments.of("/c/litmustest/singlethread/01cast.c", null), + Arguments.of("/c/litmustest/singlethread/02types.c", null), + Arguments.of("/c/litmustest/singlethread/03bitwise.c", null), + Arguments.of("/c/litmustest/singlethread/04real.c", null), + ) } - @ParameterizedTest - @MethodSource("cFiles") - fun testCWitness(filePath: String, extraArgs: String?) { - val temp = createTempDirectory() - val params = arrayOf( - "--enable-output", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), - "--output-directory", temp.absolutePathString(), - "--debug" - ) - main(params) - Assertions.assertTrue(temp.resolve("xcfa.json").exists()) - Assertions.assertTrue(temp.resolve("arg-true.dot").exists() || temp.resolve("arg-false.dot").exists()) - temp.toFile().deleteRecursively() + @JvmStatic + fun cFilesShortInt(): Stream { + return Stream.of( + Arguments.of("/c/litmustest/singlethread/01cast.c", null), + Arguments.of("/c/litmustest/singlethread/02types.c", null), + Arguments.of("/c/litmustest/singlethread/20testinline.c", null), + Arguments.of("/c/litmustest/singlethread/21namecollision.c", null), + Arguments.of("/c/litmustest/singlethread/22nondet.c", null), + ) } - @ParameterizedTest - @MethodSource("chcFiles") - fun testCHCVerify(filePath: String, chcTransformation: ChcFrontend.ChcTransformation, extraArgs: String?) { - main(arrayOf( - "--input-type", "CHC", - "--chc-transformation", chcTransformation.toString(), - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), - "--debug" - )) + @JvmStatic + fun chcFiles(): Stream { + return Stream.of( + Arguments.of( + "/chc/chc-LIA-Lin_000.smt2", + ChcFrontend.ChcTransformation.FORWARD, + "--domain PRED_CART", + ), + Arguments.of( + "/chc/chc-LIA-Arrays_000.smt2", + ChcFrontend.ChcTransformation.BACKWARD, + "--domain PRED_CART --search BFS", + ), + ) } + } - @ParameterizedTest - @MethodSource("singleThreadedCFiles") - fun testCVerifyKind(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--backend", "BOUNDED", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) - } + @ParameterizedTest + @MethodSource("cFiles") + fun testCVerifyDirect(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), + "--debug", + ) + main(params) + } - @ParameterizedTest - @MethodSource("singleThreadedCFiles") - fun testCVerifyIMC(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--backend", "BOUNDED", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) + @ParameterizedTest + @MethodSource("cFilesShort") + fun testCVerifyServer(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), + "--debug", + ) + try { + main(params) + } catch (e: IllegalStateException) { + if (!e.message.equals("Done debugging")) { + throw e + } } + } - @ParameterizedTest - @MethodSource("singleThreadedCFiles") - fun testCVerifyIMCThenKind(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--backend", "BOUNDED", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) + @ParameterizedTest + @MethodSource("cFilesShort") + fun testCVerifyPortfolio(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--input-type", + "C", + "--backend", + "PORTFOLIO", + "--portfolio", + javaClass.getResource("/simple.kts")!!.path, + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + try { + main(params) + } catch (e: Throwable) { + if (!e.toString().contains("Done debugging")) { + throw e + } } + } - @ParameterizedTest - @MethodSource("singleThreadedCFiles") - fun testCVerifyBoundedPortfolio(filePath: String, extraArgs: String?) { - val params = arrayOf( - "--backend", "PORTFOLIO", - "--portfolio", "BOUNDED", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) + @Test + fun testCVerifyBuiltInPortfolio() { + val params = + arrayOf( + "--input-type", + "C", + "--backend", + "PORTFOLIO", + "--portfolio", + "COMPLEX", + "--input", + javaClass.getResource("/c/dekker.i")!!.path, + "--stacktrace", + "--debug", + ) + try { + main(params) + } catch (e: Throwable) { + if (!e.toString().contains("Done debugging")) { + throw e + } } + } - @ParameterizedTest - @MethodSource("cFilesShortInt") - fun testCVerifyCHC(filePath: String, extraArgs: String?) { - Assumptions.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + @ParameterizedTest + @MethodSource("cFiles") + fun testCWitness(filePath: String, extraArgs: String?) { + val temp = createTempDirectory() + val params = + arrayOf( + "--enable-output", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), + "--output-directory", + temp.absolutePathString(), + "--debug", + ) + main(params) + Assertions.assertTrue(temp.resolve("xcfa.json").exists()) + Assertions.assertTrue( + temp.resolve("arg-true.dot").exists() || temp.resolve("arg-false.dot").exists() + ) + temp.toFile().deleteRecursively() + } - val params = arrayOf( - "--backend", "CHC", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) - } + @ParameterizedTest + @MethodSource("chcFiles") + fun testCHCVerify( + filePath: String, + chcTransformation: ChcFrontend.ChcTransformation, + extraArgs: String?, + ) { + main( + arrayOf( + "--input-type", + "CHC", + "--chc-transformation", + chcTransformation.toString(), + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), + "--debug", + ) + ) + } - @ParameterizedTest - @MethodSource("cFilesShortInt") - fun testCVerifyCHCPortfolio(filePath: String, extraArgs: String?) { - Assumptions.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)); + @ParameterizedTest + @MethodSource("singleThreadedCFiles") + fun testCVerifyKind(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--backend", + "BOUNDED", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } - val params = arrayOf( - "--backend", "portfolio", - "--portfolio", "HORN", - "--input-type", "C", - "--input", javaClass.getResource(filePath)!!.path, - "--stacktrace", - "--debug" - ) - main(params) - } + @ParameterizedTest + @MethodSource("singleThreadedCFiles") + fun testCVerifyIMC(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--backend", + "BOUNDED", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } + + @ParameterizedTest + @MethodSource("singleThreadedCFiles") + fun testCVerifyIMCThenKind(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--backend", + "BOUNDED", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } + + @ParameterizedTest + @MethodSource("singleThreadedCFiles") + fun testCVerifyBoundedPortfolio(filePath: String, extraArgs: String?) { + val params = + arrayOf( + "--backend", + "PORTFOLIO", + "--portfolio", + "BOUNDED", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } + + @ParameterizedTest + @MethodSource("cFilesShortInt") + fun testCVerifyCHC(filePath: String, extraArgs: String?) { + Assumptions.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) + + val params = + arrayOf( + "--backend", + "CHC", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } + + @ParameterizedTest + @MethodSource("cFilesShortInt") + fun testCVerifyCHCPortfolio(filePath: String, extraArgs: String?) { + Assumptions.assumeTrue(OsHelper.getOs().equals(OsHelper.OperatingSystem.LINUX)) + val params = + arrayOf( + "--backend", + "portfolio", + "--portfolio", + "HORN", + "--input-type", + "C", + "--input", + javaClass.getResource(filePath)!!.path, + "--stacktrace", + "--debug", + ) + main(params) + } } diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaDslTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaDslTest.kt index 53d5ba6130..89c11bb249 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaDslTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaDslTest.kt @@ -31,78 +31,87 @@ import org.junit.Test class XcfaDslTest { - private fun getSyncXcfa() = xcfa("example") { - val proc1 = procedure("proc1") { - val a = "a" type Int() direction IN - val b = "b" type Int() direction OUT + private fun getSyncXcfa() = + xcfa("example") { + val proc1 = + procedure("proc1") { + val a = "a" type Int() direction IN + val b = "b" type Int() direction OUT - (init to final) { - b assign a.ref - } + (init to final) { b assign a.ref } } - val main = procedure("main") { - val tmp = "tmp" type Int() - (init to "L1") { - proc1("1", tmp.ref) - } - ("L1" to final) { - assume("(= tmp 1)") - } - ("L1" to err) { - assume("(/= tmp 1)") - } + val main = + procedure("main") { + val tmp = "tmp" type Int() + (init to "L1") { proc1("1", tmp.ref) } + ("L1" to final) { assume("(= tmp 1)") } + ("L1" to err) { assume("(/= tmp 1)") } } - main.start() + main.start() } - private fun getAsyncXcfa() = xcfa("example") { - val proc1 = procedure("proc1") { - val a = "a" type Int() direction IN - val b = "b" type Int() direction OUT + private fun getAsyncXcfa() = + xcfa("example") { + val proc1 = + procedure("proc1") { + val a = "a" type Int() direction IN + val b = "b" type Int() direction OUT - (init to final) { - b assign a.ref - } + (init to final) { b assign a.ref } } - val main = procedure("main") { - val tmp = "tmp" type Int() - val thr1 = "thr1" type Int() - (init to "L1") { - thr1.start(proc1, "1", tmp.ref) - } - ("L1" to final) { - thr1.join() - assume("(= tmp 1)") - } - ("L1" to err) { - thr1.join() - assume("(/= tmp 1)") - } + val main = + procedure("main") { + val tmp = "tmp" type Int() + val thr1 = "thr1" type Int() + (init to "L1") { thr1.start(proc1, "1", tmp.ref) } + ("L1" to final) { + thr1.join() + assume("(= tmp 1)") + } + ("L1" to err) { + thr1.join() + assume("(/= tmp 1)") + } } - main.start() + main.start() } - @Test - fun verifyXcfa() { - SolverManager.registerSolverManager(Z3SolverManager.create()) - val config = XcfaConfig( - backendConfig = BackendConfig(backend = Backend.CEGAR, specConfig = CegarConfig())) - run { - val xcfa = getSyncXcfa() - val checker = getChecker(xcfa, emptySet(), config, ParseContext(), NullLogger.getInstance(), - NullLogger.getInstance()) - val safetyResult = checker.check() - Assert.assertTrue(safetyResult.isSafe) - } - run { - val xcfa = getAsyncXcfa() - val checker = getChecker(xcfa, emptySet(), config, ParseContext(), NullLogger.getInstance(), - NullLogger.getInstance()) - val safetyResult = checker.check() - Assert.assertTrue(safetyResult.isUnsafe) - } + @Test + fun verifyXcfa() { + SolverManager.registerSolverManager(Z3SolverManager.create()) + val config = + XcfaConfig( + backendConfig = BackendConfig(backend = Backend.CEGAR, specConfig = CegarConfig()) + ) + run { + val xcfa = getSyncXcfa() + val checker = + getChecker( + xcfa, + emptySet(), + config, + ParseContext(), + NullLogger.getInstance(), + NullLogger.getInstance(), + ) + val safetyResult = checker.check() + Assert.assertTrue(safetyResult.isSafe) } - -} \ No newline at end of file + run { + val xcfa = getAsyncXcfa() + val checker = + getChecker( + xcfa, + emptySet(), + config, + ParseContext(), + NullLogger.getInstance(), + NullLogger.getInstance(), + ) + val safetyResult = checker.check() + Assert.assertTrue(safetyResult.isUnsafe) + } + } +} diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaToCTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaToCTest.kt index 51372ee26d..c6b7f12a21 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaToCTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaToCTest.kt @@ -21,47 +21,47 @@ import hu.bme.mit.theta.frontend.chc.ChcFrontend import hu.bme.mit.theta.frontend.chc.ChcFrontend.ChcTransformation import hu.bme.mit.theta.xcfa.passes.ChcPasses import hu.bme.mit.theta.xcfa.toC +import java.io.FileInputStream +import java.util.stream.Stream +import kotlin.io.path.createTempDirectory import org.antlr.v4.runtime.CharStreams import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -import java.io.FileInputStream -import java.util.stream.Stream -import kotlin.io.path.createTempDirectory class XcfaToCTest { - companion object { - - @JvmStatic - fun chcFiles(): Stream { - return Stream.of( - Arguments.of("/chc/chc-LIA-Lin-Arrays_000.smt2", ChcFrontend.ChcTransformation.FORWARD), - Arguments.of("/chc/chc-LIA-Arrays_000.smt2", ChcFrontend.ChcTransformation.BACKWARD), - ) - } + companion object { - @JvmStatic - fun dslFiles(): Stream { - return Stream.of( - Arguments.of("/dsl/async.xcfa.kts"), - Arguments.of("/dsl/sync.xcfa.kts"), - ) - } + @JvmStatic + fun chcFiles(): Stream { + return Stream.of( + Arguments.of("/chc/chc-LIA-Lin-Arrays_000.smt2", ChcFrontend.ChcTransformation.FORWARD), + Arguments.of("/chc/chc-LIA-Arrays_000.smt2", ChcFrontend.ChcTransformation.BACKWARD), + ) } - @ParameterizedTest - @MethodSource("chcFiles") - fun testRoundTrip(filePath: String, chcTransformation: ChcTransformation) { - val chcFrontend = ChcFrontend(chcTransformation) - val xcfa = chcFrontend.buildXcfa( - CharStreams.fromStream(FileInputStream(javaClass.getResource(filePath)!!.path)), ChcPasses( - ParseContext(), NullLogger.getInstance())).build() - val temp = createTempDirectory() - val file = temp.resolve("${filePath.split("/").last()}.c").also { - it.toFile().writeText(xcfa.toC(ParseContext(), - true, false, false)) - } - System.err.println(file) + @JvmStatic + fun dslFiles(): Stream { + return Stream.of(Arguments.of("/dsl/async.xcfa.kts"), Arguments.of("/dsl/sync.xcfa.kts")) } + } + @ParameterizedTest + @MethodSource("chcFiles") + fun testRoundTrip(filePath: String, chcTransformation: ChcTransformation) { + val chcFrontend = ChcFrontend(chcTransformation) + val xcfa = + chcFrontend + .buildXcfa( + CharStreams.fromStream(FileInputStream(javaClass.getResource(filePath)!!.path)), + ChcPasses(ParseContext(), NullLogger.getInstance()), + ) + .build() + val temp = createTempDirectory() + val file = + temp.resolve("${filePath.split("/").last()}.c").also { + it.toFile().writeText(xcfa.toC(ParseContext(), true, false, false)) + } + System.err.println(file) + } } diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts b/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts index fb98c7344d..5014004377 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import hu.bme.mit.theta.analysis.expr.refinement.PruneStrategy import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.frontend.ParseContext @@ -29,81 +28,108 @@ import hu.bme.mit.theta.xcfa.passes.LbePass import java.nio.file.Paths fun portfolio( - xcfa: XCFA, - mcm: MCM, - parseContext: ParseContext, - portfolioConfig: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger): STM { + xcfa: XCFA, + mcm: MCM, + parseContext: ParseContext, + portfolioConfig: XcfaConfig<*, *>, + logger: Logger, + uniqueLogger: Logger, +): STM { - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } - var baseConfig = XcfaConfig( - inputConfig = InputConfig( - input = null, - xcfaWCtx = Triple(xcfa, mcm, parseContext), - propertyFile = null, - property = portfolioConfig.inputConfig.property), - frontendConfig = FrontendConfig( - lbeLevel = LbePass.LbeLevel.LBE_SEQ, - loopUnroll = 50, - inputType = InputType.C, - specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient)), - backendConfig = BackendConfig( - backend = Backend.CEGAR, - solverHome = portfolioConfig.backendConfig.solverHome, - timeoutMs = 0, - specConfig = CegarConfig( - initPrec = InitPrec.EMPTY, - porLevel = POR.NOPOR, - porRandomSeed = -1, - coi = ConeOfInfluenceMode.NO_COI, - cexMonitor = CexMonitorOptions.CHECK, - abstractorConfig = CegarAbstractorConfig( - abstractionSolver = "Z3", - validateAbstractionSolver = false, - domain = Domain.EXPL, - maxEnum = 1, - search = Search.ERR + var baseConfig = + XcfaConfig( + inputConfig = + InputConfig( + input = null, + xcfaWCtx = Triple(xcfa, mcm, parseContext), + propertyFile = null, + property = portfolioConfig.inputConfig.property, + ), + frontendConfig = + FrontendConfig( + lbeLevel = LbePass.LbeLevel.LBE_SEQ, + loopUnroll = 50, + inputType = InputType.C, + specConfig = CFrontendConfig(arithmetic = ArchitectureConfig.ArithmeticType.efficient), + ), + backendConfig = + BackendConfig( + backend = Backend.CEGAR, + solverHome = portfolioConfig.backendConfig.solverHome, + timeoutMs = 0, + specConfig = + CegarConfig( + initPrec = InitPrec.EMPTY, + porLevel = POR.NOPOR, + porRandomSeed = -1, + coi = ConeOfInfluenceMode.NO_COI, + cexMonitor = CexMonitorOptions.CHECK, + abstractorConfig = + CegarAbstractorConfig( + abstractionSolver = "Z3", + validateAbstractionSolver = false, + domain = Domain.EXPL, + maxEnum = 1, + search = Search.ERR, ), - refinerConfig = CegarRefinerConfig( - refinementSolver = "Z3", - validateRefinementSolver = false, - refinement = Refinement.SEQ_ITP, - exprSplitter = ExprSplitterOptions.WHOLE, - pruneStrategy = PruneStrategy.FULL - ))), - outputConfig = OutputConfig( - versionInfo = false, - resultFolder = Paths.get("./").toFile(), // cwd - cOutputConfig = COutputConfig(disable = true), - witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true) + refinerConfig = + CegarRefinerConfig( + refinementSolver = "Z3", + validateRefinementSolver = false, + refinement = Refinement.SEQ_ITP, + exprSplitter = ExprSplitterOptions.WHOLE, + pruneStrategy = PruneStrategy.FULL, + ), + ), + ), + outputConfig = + OutputConfig( + versionInfo = false, + resultFolder = Paths.get("./").toFile(), // cwd + cOutputConfig = COutputConfig(disable = true), + witnessConfig = + WitnessConfig( + disable = false, + concretizerSolver = "Z3", + validateConcretizerSolver = false, + ), + argConfig = ArgConfig(disable = true), ), - debugConfig = portfolioConfig.debugConfig + debugConfig = portfolioConfig.debugConfig, ) - if (parseContext.multiThreading) { - val baseCegarConfig = baseConfig.backendConfig.specConfig!! - val multiThreadedCegarConfig = baseCegarConfig.copy( - coi = if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) ConeOfInfluenceMode.NO_COI else ConeOfInfluenceMode.COI, - porLevel = if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, - abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.DFS), - ) - baseConfig = baseConfig.copy( - backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig)) - } + if (parseContext.multiThreading) { + val baseCegarConfig = baseConfig.backendConfig.specConfig!! + val multiThreadedCegarConfig = + baseCegarConfig.copy( + coi = + if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) + ConeOfInfluenceMode.NO_COI + else ConeOfInfluenceMode.COI, + porLevel = + if (baseConfig.inputConfig.property == ErrorDetection.DATA_RACE) POR.SPOR else POR.AASPOR, + abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.DFS), + ) + baseConfig = + baseConfig.copy( + backendConfig = baseConfig.backendConfig.copy(specConfig = multiThreadedCegarConfig) + ) + } - if (!xcfa.isInlined) { - val baseCegarConfig = baseConfig.backendConfig.specConfig!! - val recursiveConfig = baseCegarConfig.copy( - abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), - refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY) - ) - baseConfig = baseConfig.copy(backendConfig = baseConfig.backendConfig.copy(specConfig = recursiveConfig)) - } + if (!xcfa.isInlined) { + val baseCegarConfig = baseConfig.backendConfig.specConfig!! + val recursiveConfig = + baseCegarConfig.copy( + abstractorConfig = baseCegarConfig.abstractorConfig.copy(search = Search.BFS), + refinerConfig = baseCegarConfig.refinerConfig.copy(pruneStrategy = PruneStrategy.LAZY), + ) + baseConfig = + baseConfig.copy(backendConfig = baseConfig.backendConfig.copy(specConfig = recursiveConfig)) + } - return STM(ConfigNode("BaseConfig", baseConfig, checker), setOf()) + return STM(ConfigNode("BaseConfig", baseConfig, checker), setOf()) } -portfolio(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) \ No newline at end of file +portfolio(xcfa, mcm, parseContext, portfolioConfig, logger, uniqueLogger) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/Utils.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/Utils.kt index cc6f24571b..f48bd757d8 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/Utils.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/Utils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa import com.google.common.base.Preconditions.checkState @@ -45,59 +44,66 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType import hu.bme.mit.theta.xcfa.model.* import java.util.function.Predicate - -/** - * Get flattened label list (without sequence labels). - */ +/** Get flattened label list (without sequence labels). */ fun XcfaEdge.getFlatLabels(): List = label.getFlatLabels() -fun XcfaLabel.getFlatLabels(): List = when (this) { +fun XcfaLabel.getFlatLabels(): List = + when (this) { is SequenceLabel -> { - val ret = mutableListOf() - labels.forEach { ret.addAll(it.getFlatLabels()) } - ret + val ret = mutableListOf() + labels.forEach { ret.addAll(it.getFlatLabels()) } + ret } else -> listOf(this) -} + } -fun XCFA.collectVars(): Iterable> = vars.map { it.wrappedVar } union procedures.map { it.vars }.flatten() +fun XCFA.collectVars(): Iterable> = + vars.map { it.wrappedVar } union procedures.map { it.vars }.flatten() -fun XCFA.collectAssumes(): Iterable> = procedures.map { procedure -> - procedure.edges.map { it.label.collectAssumes() }.flatten() -}.flatten() +fun XCFA.collectAssumes(): Iterable> = + procedures + .map { procedure -> procedure.edges.map { it.label.collectAssumes() }.flatten() } + .flatten() -fun XcfaLabel.collectAssumes(): Iterable> = when (this) { - is StmtLabel -> when (stmt) { +fun XcfaLabel.collectAssumes(): Iterable> = + when (this) { + is StmtLabel -> + when (stmt) { is AssumeStmt -> setOf(stmt.cond) else -> setOf() - } + } is NondetLabel -> labels.map { it.collectAssumes() }.flatten() is SequenceLabel -> labels.map { it.collectAssumes() }.flatten() else -> setOf() -} + } fun XcfaLabel.collectAssumesVars(): Set> { - return collectAssumes().flatMap { - val v = mutableSetOf>() - ExprUtils.collectVars(it, v) - v - }.toSet() + return collectAssumes() + .flatMap { + val v = mutableSetOf>() + ExprUtils.collectVars(it, v) + v + } + .toSet() } -fun XcfaLabel.collectHavocs(): Set> = when (this) { - is StmtLabel -> when (stmt) { +fun XcfaLabel.collectHavocs(): Set> = + when (this) { + is StmtLabel -> + when (stmt) { is HavocStmt<*> -> setOf(stmt) else -> setOf() - } + } is NondetLabel -> labels.map { it.collectHavocs() }.flatten().toSet() is SequenceLabel -> labels.map { it.collectHavocs() }.flatten().toSet() else -> setOf() -} + } -fun XcfaLabel.collectVars(): Iterable> = when (this) { +fun XcfaLabel.collectVars(): Iterable> = + when (this) { is StmtLabel -> StmtUtils.getVars(stmt) is NondetLabel -> labels.map { it.collectVars() }.flatten() is SequenceLabel -> labels.map { it.collectVars() }.flatten() @@ -107,146 +113,168 @@ fun XcfaLabel.collectVars(): Iterable> = when (this) { is StartLabel -> params.map { ExprUtils.getVars(it) }.flatten().toSet() union setOf(pidVar) is WriteLabel -> setOf(global, local) else -> emptySet() -} + } // Complex var access requests typealias AccessType = Pair + typealias VarAccessMap = Map, AccessType> -val AccessType?.isRead get() = this?.first == true -val AccessType?.isWritten get() = this?.second == true +val AccessType?.isRead + get() = this?.first == true +val AccessType?.isWritten + get() = this?.second == true + fun AccessType?.merge(other: AccessType?) = - Pair(this?.first == true || other?.first == true, this?.second == true || other?.second == true) + Pair(this?.first == true || other?.first == true, this?.second == true || other?.second == true) -val WRITE: AccessType get() = Pair(false, true) -val READ: AccessType get() = Pair(true, false) +val WRITE: AccessType + get() = Pair(false, true) +val READ: AccessType + get() = Pair(true, false) -private fun List.mergeAndCollect(): VarAccessMap = this.fold(mapOf()) { acc, next -> +private fun List.mergeAndCollect(): VarAccessMap = + this.fold(mapOf()) { acc, next -> (acc.keys + next.keys).associateWith { acc[it].merge(next[it]) } -} + } private operator fun VarAccessMap?.plus(other: VarAccessMap?): VarAccessMap = - listOfNotNull(this, other).mergeAndCollect() + listOfNotNull(this, other).mergeAndCollect() -inline val XcfaLabel.isAtomicBegin: Boolean get() = this is FenceLabel && "ATOMIC_BEGIN" in labels -inline val XcfaLabel.isAtomicEnd: Boolean get() = this is FenceLabel && "ATOMIC_END" in labels +inline val XcfaLabel.isAtomicBegin: Boolean + get() = this is FenceLabel && "ATOMIC_BEGIN" in labels +inline val XcfaLabel.isAtomicEnd: Boolean + get() = this is FenceLabel && "ATOMIC_END" in labels -/** - * The set of mutexes acquired by the label. - */ -inline val FenceLabel.acquiredMutexes: Set get() = labels.mapNotNull { it.acquiredMutex }.toSet() +/** The set of mutexes acquired by the label. */ +inline val FenceLabel.acquiredMutexes: Set + get() = labels.mapNotNull { it.acquiredMutex }.toSet() inline val String.acquiredMutex: String? - get() = when { - this == "ATOMIC_BEGIN" -> "" - startsWith("mutex_lock") -> substringAfter('(').substringBefore(')') - startsWith("cond_wait") -> substring("cond_wait".length + 1, length - 1).split(",")[1] - else -> null + get() = + when { + this == "ATOMIC_BEGIN" -> "" + startsWith("mutex_lock") -> substringAfter('(').substringBefore(')') + startsWith("cond_wait") -> substring("cond_wait".length + 1, length - 1).split(",")[1] + else -> null } -/** - * The set of mutexes released by the label. - */ -inline val FenceLabel.releasedMutexes: Set get() = labels.mapNotNull { it.releasedMutex }.toSet() +/** The set of mutexes released by the label. */ +inline val FenceLabel.releasedMutexes: Set + get() = labels.mapNotNull { it.releasedMutex }.toSet() inline val String.releasedMutex: String? - get() = when { - this == "ATOMIC_END" -> "" - startsWith("mutex_unlock") -> substringAfter('(').substringBefore(')') - startsWith("start_cond_wait") -> substring("start_cond_wait".length + 1, length - 1).split(",")[1] - else -> null + get() = + when { + this == "ATOMIC_END" -> "" + startsWith("mutex_unlock") -> substringAfter('(').substringBefore(')') + startsWith("start_cond_wait") -> + substring("start_cond_wait".length + 1, length - 1).split(",")[1] + else -> null } -/** - * The set of mutexes acquired embedded into each other. - */ +/** The set of mutexes acquired embedded into each other. */ inline val XcfaEdge.acquiredEmbeddedFenceVars: Set - get() { - val acquired = mutableSetOf() - val toVisit = mutableListOf>>(this to setOf()) - while (toVisit.isNotEmpty()) { - val (visiting, mutexes) = toVisit.removeFirst() - val newMutexes = mutexes.toMutableSet() - acquired.addAll(visiting.getFlatLabels().flatMap { fence -> - if (fence !is FenceLabel) return@flatMap emptyList() - fence.acquiredMutexes + fence.labels.filter { it.startsWith("start_cond_wait") } - .map { it.substring("start_cond_wait".length + 1, it.length - 1).split(",")[0] } - }) - if (visiting.mutexOperations(newMutexes)) { - visiting.target.outgoingEdges.forEach { toVisit.add(it to newMutexes) } - } + get() { + val acquired = mutableSetOf() + val toVisit = mutableListOf>>(this to setOf()) + while (toVisit.isNotEmpty()) { + val (visiting, mutexes) = toVisit.removeFirst() + val newMutexes = mutexes.toMutableSet() + acquired.addAll( + visiting.getFlatLabels().flatMap { fence -> + if (fence !is FenceLabel) return@flatMap emptyList() + fence.acquiredMutexes + + fence.labels + .filter { it.startsWith("start_cond_wait") } + .map { it.substring("start_cond_wait".length + 1, it.length - 1).split(",")[0] } } - return acquired + ) + if (visiting.mutexOperations(newMutexes)) { + visiting.target.outgoingEdges.forEach { toVisit.add(it to newMutexes) } + } } + return acquired + } -/** - * Returns the list of accessed variables by the edge associated with an AccessType object. - */ +/** Returns the list of accessed variables by the edge associated with an AccessType object. */ fun XcfaEdge.collectVarsWithAccessType(): VarAccessMap = label.collectVarsWithAccessType() /** - * Returns the list of accessed variables by the label. - * The variable is associated with an AccessType object based on whether the variable is read/written by the label. + * Returns the list of accessed variables by the label. The variable is associated with an + * AccessType object based on whether the variable is read/written by the label. */ -fun XcfaLabel.collectVarsWithAccessType(): VarAccessMap = when (this) { +fun XcfaLabel.collectVarsWithAccessType(): VarAccessMap = + when (this) { is StmtLabel -> { - when (stmt) { - is HavocStmt<*> -> mapOf(stmt.varDecl to WRITE) - is AssignStmt<*> -> ExprUtils.getVars(stmt.expr).associateWith { READ } + mapOf(stmt.varDecl to WRITE) - is MemoryAssignStmt<*, *, *> -> { - var expr: Expr<*> = stmt.deref - while (expr is Dereference<*, *, *>) { - expr = expr.array - } - ExprUtils.getVars(stmt.expr).associateWith { READ } + when (expr) { - is RefExpr<*> -> mapOf(expr.decl as VarDecl<*> to READ) // the memory address is read, not written - is LitExpr<*> -> mapOf() - else -> error("MemoryAssignStmts's dereferences should only contain refs or lits") - } + when (stmt) { + is HavocStmt<*> -> mapOf(stmt.varDecl to WRITE) + is AssignStmt<*> -> + ExprUtils.getVars(stmt.expr).associateWith { READ } + mapOf(stmt.varDecl to WRITE) + is MemoryAssignStmt<*, *, *> -> { + var expr: Expr<*> = stmt.deref + while (expr is Dereference<*, *, *>) { + expr = expr.array + } + ExprUtils.getVars(stmt.expr).associateWith { READ } + + when (expr) { + is RefExpr<*> -> + mapOf(expr.decl as VarDecl<*> to READ) // the memory address is read, not written + is LitExpr<*> -> mapOf() + else -> error("MemoryAssignStmts's dereferences should only contain refs or lits") } - - else -> StmtUtils.getVars(stmt).associateWith { READ } } + + else -> StmtUtils.getVars(stmt).associateWith { READ } + } } is NondetLabel -> labels.map { it.collectVarsWithAccessType() }.mergeAndCollect() is SequenceLabel -> labels.map { it.collectVarsWithAccessType() }.mergeAndCollect() - is InvokeLabel -> params.map { ExprUtils.getVars(it) }.flatten().associateWith { READ } // TODO is it read? - is StartLabel -> params.map { ExprUtils.getVars(it) }.flatten().associateWith { READ } + mapOf(pidVar to READ) + is InvokeLabel -> + params.map { ExprUtils.getVars(it) }.flatten().associateWith { READ } // TODO is it read? + is StartLabel -> + params.map { ExprUtils.getVars(it) }.flatten().associateWith { READ } + mapOf(pidVar to READ) is JoinLabel -> mapOf(pidVar to READ) is ReadLabel -> mapOf(global to READ, local to READ) is WriteLabel -> mapOf(global to WRITE, local to WRITE) else -> emptyMap() -} + } /** * Returns the global variables accessed by the label (the variables present in the given argument). */ private fun XcfaLabel.collectGlobalVars(globalVars: Set>): VarAccessMap = - collectVarsWithAccessType().filter { labelVar -> globalVars.any { it == labelVar.key } } + collectVarsWithAccessType().filter { labelVar -> globalVars.any { it == labelVar.key } } /** - * Returns the global variables (potentially indirectly) accessed by the edge. - * If the edge starts an atomic block, all variable accesses in the atomic block are returned. - * Variables are associated with a pair of boolean values: the first is true if the variable is read and false otherwise. The second is similar for write access. + * Returns the global variables (potentially indirectly) accessed by the edge. If the edge starts an + * atomic block, all variable accesses in the atomic block are returned. Variables are associated + * with a pair of boolean values: the first is true if the variable is read and false otherwise. The + * second is similar for write access. */ fun XcfaEdge.collectIndirectGlobalVarAccesses(xcfa: XCFA): VarAccessMap { - val globalVars = xcfa.vars.map(XcfaGlobalVar::wrappedVar).toSet() - val flatLabels = getFlatLabels() - val mutexes = flatLabels.filterIsInstance().flatMap { it.acquiredMutexes }.toMutableSet() - return if (mutexes.isEmpty()) { - label.collectGlobalVars(globalVars) - } else { - collectGlobalVarsWithTraversal(globalVars) { it.mutexOperations(mutexes) } - } + val globalVars = xcfa.vars.map(XcfaGlobalVar::wrappedVar).toSet() + val flatLabels = getFlatLabels() + val mutexes = + flatLabels.filterIsInstance().flatMap { it.acquiredMutexes }.toMutableSet() + return if (mutexes.isEmpty()) { + label.collectGlobalVars(globalVars) + } else { + collectGlobalVarsWithTraversal(globalVars) { it.mutexOperations(mutexes) } + } } /** - * Represents a global variable access: stores the variable declaration, the access type (read/write) and the set of - * mutexes that are needed to perform the variable access. + * Represents a global variable access: stores the variable declaration, the access type + * (read/write) and the set of mutexes that are needed to perform the variable access. */ -class GlobalVarAccessWithMutexes(val varDecl: VarDecl<*>, val access: AccessType, val mutexes: Set) +class GlobalVarAccessWithMutexes( + val varDecl: VarDecl<*>, + val access: AccessType, + val mutexes: Set, +) /** * Returns the global variable accesses of the edge. @@ -255,25 +283,34 @@ class GlobalVarAccessWithMutexes(val varDecl: VarDecl<*>, val access: AccessType * @param currentMutexes the set of mutexes currently acquired by the process of the edge * @return the list of global variable accesses (c.f., [GlobalVarAccessWithMutexes]) */ -fun XcfaEdge.getGlobalVarsWithNeededMutexes(xcfa: XCFA, currentMutexes: Set): List { - val globalVars = xcfa.vars.map(XcfaGlobalVar::wrappedVar).toSet() - val neededMutexes = currentMutexes.toMutableSet() - val accesses = mutableListOf() - getFlatLabels().forEach { label -> - if (label is FenceLabel) { - neededMutexes.addAll(label.acquiredMutexes) - } else { - val vars = label.collectGlobalVars(globalVars) - accesses.addAll(vars.mapNotNull { (varDecl, accessType) -> - if (accesses.any { it.varDecl == varDecl && (it.access == accessType && it.access == WRITE) }) { - null - } else { - GlobalVarAccessWithMutexes(varDecl, accessType, neededMutexes.toSet()) - } - }) +fun XcfaEdge.getGlobalVarsWithNeededMutexes( + xcfa: XCFA, + currentMutexes: Set, +): List { + val globalVars = xcfa.vars.map(XcfaGlobalVar::wrappedVar).toSet() + val neededMutexes = currentMutexes.toMutableSet() + val accesses = mutableListOf() + getFlatLabels().forEach { label -> + if (label is FenceLabel) { + neededMutexes.addAll(label.acquiredMutexes) + } else { + val vars = label.collectGlobalVars(globalVars) + accesses.addAll( + vars.mapNotNull { (varDecl, accessType) -> + if ( + accesses.any { + it.varDecl == varDecl && (it.access == accessType && it.access == WRITE) + } + ) { + null + } else { + GlobalVarAccessWithMutexes(varDecl, accessType, neededMutexes.toSet()) + } } + ) } - return accesses + } + return accesses } /** @@ -282,27 +319,29 @@ fun XcfaEdge.getGlobalVarsWithNeededMutexes(xcfa: XCFA, currentMutexes: Set>, goFurther: Predicate) - : VarAccessMap { - val vars = mutableMapOf, AccessType>() - val exploredEdges = mutableListOf() - val edgesToExplore = mutableListOf() - edgesToExplore.add(this) - while (edgesToExplore.isNotEmpty()) { - val exploring = edgesToExplore.removeFirst() - exploring.label.collectGlobalVars(globalVars).forEach { (varDecl, access) -> - vars[varDecl] = vars[varDecl].merge(access) - } - if (goFurther.test(exploring)) { - for (newEdge in exploring.target.outgoingEdges) { - if (newEdge !in exploredEdges) { - edgesToExplore.add(newEdge) - } - } +private fun XcfaEdge.collectGlobalVarsWithTraversal( + globalVars: Set>, + goFurther: Predicate, +): VarAccessMap { + val vars = mutableMapOf, AccessType>() + val exploredEdges = mutableListOf() + val edgesToExplore = mutableListOf() + edgesToExplore.add(this) + while (edgesToExplore.isNotEmpty()) { + val exploring = edgesToExplore.removeFirst() + exploring.label.collectGlobalVars(globalVars).forEach { (varDecl, access) -> + vars[varDecl] = vars[varDecl].merge(access) + } + if (goFurther.test(exploring)) { + for (newEdge in exploring.target.outgoingEdges) { + if (newEdge !in exploredEdges) { + edgesToExplore.add(newEdge) } - exploredEdges.add(exploring) + } } - return vars + exploredEdges.add(exploring) + } + return vars } /** @@ -312,283 +351,371 @@ private fun XcfaEdge.collectGlobalVarsWithTraversal(globalVars: Set>, * @return true if the set of mutexes is non-empty after the mutex operations */ fun XcfaEdge.mutexOperations(mutexes: MutableSet): Boolean { - val edgeFlatLabels = getFlatLabels() - val acquiredLocks = mutableSetOf() - val releasedLocks = mutableSetOf() - edgeFlatLabels.filterIsInstance().forEach { fence -> - releasedLocks.addAll(fence.releasedMutexes) - acquiredLocks.removeAll(fence.releasedMutexes) - - acquiredLocks.addAll(fence.acquiredMutexes) - releasedLocks.removeAll(fence.acquiredMutexes) - } - mutexes.removeAll(releasedLocks) - mutexes.addAll(acquiredLocks) - return mutexes.isNotEmpty() + val edgeFlatLabels = getFlatLabels() + val acquiredLocks = mutableSetOf() + val releasedLocks = mutableSetOf() + edgeFlatLabels.filterIsInstance().forEach { fence -> + releasedLocks.addAll(fence.releasedMutexes) + acquiredLocks.removeAll(fence.releasedMutexes) + + acquiredLocks.addAll(fence.acquiredMutexes) + releasedLocks.removeAll(fence.acquiredMutexes) + } + mutexes.removeAll(releasedLocks) + mutexes.addAll(acquiredLocks) + return mutexes.isNotEmpty() } fun XCFA.getSymbols(): Pair { - val symbolTable = SymbolTable() - val scope = XcfaScope(symbolTable) - val vars = collectVars() - val env = Env() - vars.forEach { - val symbol = Symbol { it.name } - symbolTable.add(symbol) - env.define(symbol, it) - } - return Pair(scope, env) + val symbolTable = SymbolTable() + val scope = XcfaScope(symbolTable) + val vars = collectVars() + val env = Env() + vars.forEach { + val symbol = Symbol { it.name } + symbolTable.add(symbol) + env.define(symbol, it) + } + return Pair(scope, env) } /** - * Returns XCFA locations that are inner locations of any atomic block (after an edge with an AtomicBegin and before - * an edge with an AtomicEnd). + * Returns XCFA locations that are inner locations of any atomic block (after an edge with an + * AtomicBegin and before an edge with an AtomicEnd). * * @param builder the atomic block inner locations of the procedure of this builder will be returned * @return the list of locations in an atomic block */ fun getAtomicBlockInnerLocations(builder: XcfaProcedureBuilder): List = - getAtomicBlockInnerLocations(builder.initLoc) + getAtomicBlockInnerLocations(builder.initLoc) private fun getAtomicBlockInnerLocations(initialLocation: XcfaLocation): List { - val atomicLocations = mutableListOf() - val visitedLocations = mutableListOf() - val locationsToVisit = mutableListOf(initialLocation) - val isAtomic = mutableMapOf(initialLocation to false) - while (locationsToVisit.isNotEmpty()) { - val visiting = locationsToVisit.removeAt(0) - if (checkNotNull(isAtomic[visiting])) atomicLocations.add(visiting) - visitedLocations.add(visiting) - for (outEdge in visiting.outgoingEdges) { - var isNextAtomic = checkNotNull(isAtomic[visiting]) - if (outEdge.getFlatLabels().any { it.isAtomicBegin }) { - isNextAtomic = true - } - if (outEdge.getFlatLabels().any { it.isAtomicEnd }) { - isNextAtomic = false - } - val target = outEdge.target - isAtomic[target] = isNextAtomic - if (target in atomicLocations && !isNextAtomic) { - atomicLocations.remove(target) - } - if (target !in locationsToVisit && target !in visitedLocations) { - locationsToVisit.add(outEdge.target) - } - } + val atomicLocations = mutableListOf() + val visitedLocations = mutableListOf() + val locationsToVisit = mutableListOf(initialLocation) + val isAtomic = mutableMapOf(initialLocation to false) + while (locationsToVisit.isNotEmpty()) { + val visiting = locationsToVisit.removeAt(0) + if (checkNotNull(isAtomic[visiting])) atomicLocations.add(visiting) + visitedLocations.add(visiting) + for (outEdge in visiting.outgoingEdges) { + var isNextAtomic = checkNotNull(isAtomic[visiting]) + if (outEdge.getFlatLabels().any { it.isAtomicBegin }) { + isNextAtomic = true + } + if (outEdge.getFlatLabels().any { it.isAtomicEnd }) { + isNextAtomic = false + } + val target = outEdge.target + isAtomic[target] = isNextAtomic + if (target in atomicLocations && !isNextAtomic) { + atomicLocations.remove(target) + } + if (target !in locationsToVisit && target !in visitedLocations) { + locationsToVisit.add(outEdge.target) + } } - return atomicLocations + } + return atomicLocations } val XcfaLabel.references: List> - get() = when (this) { - is StmtLabel -> when (stmt) { - is AssumeStmt -> stmt.cond.references - is AssignStmt<*> -> stmt.expr.references - is MemoryAssignStmt<*, *, *> -> stmt.deref.references + stmt.expr.references - else -> emptyList() + get() = + when (this) { + is StmtLabel -> + when (stmt) { + is AssumeStmt -> stmt.cond.references + is AssignStmt<*> -> stmt.expr.references + is MemoryAssignStmt<*, *, *> -> stmt.deref.references + stmt.expr.references + else -> emptyList() } - is InvokeLabel -> params.flatMap { it.references } - is NondetLabel -> labels.flatMap { it.references } - is SequenceLabel -> labels.flatMap { it.references } - is StartLabel -> params.flatMap { it.references } - else -> emptyList() + is InvokeLabel -> params.flatMap { it.references } + is NondetLabel -> labels.flatMap { it.references } + is SequenceLabel -> labels.flatMap { it.references } + is StartLabel -> params.flatMap { it.references } + else -> emptyList() } val Expr<*>.references: List> - get() = if (this is Reference<*, *>) { - listOf(this) + this.ops.flatMap { it.references } + get() = + if (this is Reference<*, *>) { + listOf(this) + this.ops.flatMap { it.references } } else { - ops.flatMap { it.references } + ops.flatMap { it.references } } val XcfaLabel.dereferences: List> - get() = when (this) { - is StmtLabel -> stmt.dereferences - is InvokeLabel -> params.flatMap { it.dereferences } - is NondetLabel -> labels.flatMap { it.dereferences } - is SequenceLabel -> labels.flatMap { it.dereferences } - is StartLabel -> params.flatMap { it.dereferences } - else -> emptyList() + get() = + when (this) { + is StmtLabel -> stmt.dereferences + is InvokeLabel -> params.flatMap { it.dereferences } + is NondetLabel -> labels.flatMap { it.dereferences } + is SequenceLabel -> labels.flatMap { it.dereferences } + is StartLabel -> params.flatMap { it.dereferences } + else -> emptyList() } val Stmt.dereferences: List> - get() = when (this) { - is AssumeStmt -> cond.dereferences - is AssignStmt<*> -> expr.dereferences - is MemoryAssignStmt<*, *, *> -> expr.dereferences + listOf(deref) - else -> emptyList() + get() = + when (this) { + is AssumeStmt -> cond.dereferences + is AssignStmt<*> -> expr.dereferences + is MemoryAssignStmt<*, *, *> -> expr.dereferences + listOf(deref) + else -> emptyList() } val Expr<*>.dereferences: List> - get() = if (this is Dereference<*, *, *>) { - ops.flatMap { it.dereferences } + listOf(this) + get() = + if (this is Dereference<*, *, *>) { + ops.flatMap { it.dereferences } + listOf(this) } else { - ops.flatMap { it.dereferences } + ops.flatMap { it.dereferences } } val XcfaLabel.dereferencesWithAccessTypes: List, AccessType>> - get() = when (this) { - is NondetLabel -> error("NondetLabel is not well-defined for dereferences due to ordering") - is SequenceLabel -> labels.flatMap(XcfaLabel::dereferencesWithAccessTypes) - is InvokeLabel -> params.flatMap { it.dereferences.map { Pair(it, READ) } } - is StartLabel -> params.flatMap { it.dereferences.map { Pair(it, READ) } } - is StmtLabel -> stmt.dereferencesWithAccessTypes - - else -> listOf() + get() = + when (this) { + is NondetLabel -> error("NondetLabel is not well-defined for dereferences due to ordering") + is SequenceLabel -> labels.flatMap(XcfaLabel::dereferencesWithAccessTypes) + is InvokeLabel -> params.flatMap { it.dereferences.map { Pair(it, READ) } } + is StartLabel -> params.flatMap { it.dereferences.map { Pair(it, READ) } } + is StmtLabel -> stmt.dereferencesWithAccessTypes + + else -> listOf() } val Stmt.dereferencesWithAccessTypes: List, AccessType>> - get() = when (this) { - is MemoryAssignStmt<*, *, *> -> expr.dereferences.map { Pair(it, READ) } + listOf(Pair(deref, WRITE)) - is AssignStmt<*> -> expr.dereferences.map { Pair(it, READ) } - is AssumeStmt -> cond.dereferences.map { Pair(it, READ) } - else -> listOf() + get() = + when (this) { + is MemoryAssignStmt<*, *, *> -> + expr.dereferences.map { Pair(it, READ) } + listOf(Pair(deref, WRITE)) + is AssignStmt<*> -> expr.dereferences.map { Pair(it, READ) } + is AssumeStmt -> cond.dereferences.map { Pair(it, READ) } + else -> listOf() } -fun XcfaLabel.simplify(valuation: MutableValuation, parseContext: ParseContext): XcfaLabel = if (this is StmtLabel) { +fun XcfaLabel.simplify(valuation: MutableValuation, parseContext: ParseContext): XcfaLabel = + if (this is StmtLabel) { val simplified = stmt.accept(StmtSimplifier.StmtSimplifierVisitor(), valuation).stmt when (stmt) { - is MemoryAssignStmt<*, *, *> -> { - simplified as MemoryAssignStmt<*, *, *> - if (parseContext.metadata.getMetadataValue(stmt.expr, "cType").isPresent) - parseContext.metadata.create(simplified.expr, "cType", - CComplexType.getType(stmt.expr, parseContext)) - if (parseContext.metadata.getMetadataValue(stmt.deref, "cType").isPresent) - parseContext.metadata.create(simplified.deref, "cType", - CComplexType.getType(stmt.deref, parseContext)) - StmtLabel(simplified, metadata = metadata) - } - - is AssignStmt<*> -> { - simplified as AssignStmt<*> - if (parseContext.metadata.getMetadataValue(stmt.expr, "cType").isPresent) - parseContext.metadata.create(simplified.expr, "cType", - CComplexType.getType(stmt.expr, parseContext)) - StmtLabel(simplified, metadata = metadata) - } - - is AssumeStmt -> { - simplified as AssumeStmt - if (parseContext.metadata.getMetadataValue(stmt.cond, "cType").isPresent) { - parseContext.metadata.create(simplified.cond, "cType", - CComplexType.getType(stmt.cond, parseContext)) - } - parseContext.metadata.create(simplified, "cTruth", stmt.cond is NeqExpr<*>) - StmtLabel(simplified, metadata = metadata, choiceType = choiceType) + is MemoryAssignStmt<*, *, *> -> { + simplified as MemoryAssignStmt<*, *, *> + if (parseContext.metadata.getMetadataValue(stmt.expr, "cType").isPresent) + parseContext.metadata.create( + simplified.expr, + "cType", + CComplexType.getType(stmt.expr, parseContext), + ) + if (parseContext.metadata.getMetadataValue(stmt.deref, "cType").isPresent) + parseContext.metadata.create( + simplified.deref, + "cType", + CComplexType.getType(stmt.deref, parseContext), + ) + StmtLabel(simplified, metadata = metadata) + } + + is AssignStmt<*> -> { + simplified as AssignStmt<*> + if (parseContext.metadata.getMetadataValue(stmt.expr, "cType").isPresent) + parseContext.metadata.create( + simplified.expr, + "cType", + CComplexType.getType(stmt.expr, parseContext), + ) + StmtLabel(simplified, metadata = metadata) + } + + is AssumeStmt -> { + simplified as AssumeStmt + if (parseContext.metadata.getMetadataValue(stmt.cond, "cType").isPresent) { + parseContext.metadata.create( + simplified.cond, + "cType", + CComplexType.getType(stmt.cond, parseContext), + ) } + parseContext.metadata.create(simplified, "cTruth", stmt.cond is NeqExpr<*>) + StmtLabel(simplified, metadata = metadata, choiceType = choiceType) + } - else -> this + else -> this } -} else this + } else this data class MallocLitExpr(val kType: T) : NullaryExpr(), LitExpr { - override fun getType(): T = kType - override fun eval(valuation: Valuation): LitExpr = this + override fun getType(): T = kType + + override fun eval(valuation: Valuation): LitExpr = this } val XCFA.lazyPointsToGraph: Lazy, Set>>> - get() = lazy { - val attempt = Try.attempt { - fun unboxMod(e: Expr<*>): Expr<*> = if (e is ModExpr<*>) unboxMod(e.ops[0]) else e - - val bases = this.procedures.flatMap { - it.edges.flatMap { - it.getFlatLabels().flatMap { it.dereferences.map { unboxMod(it.array) } } - } - }.filter { it !is LitExpr<*> && it !is Dereference<*, *, *> }.toSet() - checkState(bases.all { it is RefExpr<*> }) - - // value assignments are either assignments, or thread start statements, or procedure invoke statements - val assignments = this.procedures.flatMap { - it.edges.flatMap { - it.getFlatLabels().filter { it is StmtLabel && it.stmt is AssignStmt<*> } - .map { (it as StmtLabel).stmt as AssignStmt<*> } - } - } - val threadStart = this.procedures.flatMap { - it.edges.flatMap { it.getFlatLabels().filterIsInstance() }.flatMap { - val calledProc = this.procedures.find { proc -> proc.name == it.name } - calledProc?.let { proc -> - proc.params.withIndex().filter { (_, it) -> it.second != ParamDirection.OUT }.map { (i, pair) -> - val (param, _) = pair - Assign(cast(param, param.type), cast(it.params[i], param.type)) - } + - proc.params.withIndex() - .filter { (i, pair) -> pair.second != ParamDirection.IN && it.params[i] is RefExpr<*> } - .map { (i, pair) -> - val (param, _) = pair - Assign(cast((it.params[i] as RefExpr<*>).decl as VarDecl<*>, param.type), - cast(param.ref, param.type)) - } - } ?: listOf() - } - } - val procInvoke = this.procedures.flatMap { - it.edges.flatMap { it.getFlatLabels().filterIsInstance() }.flatMap { - val calledProc = this.procedures.find { proc -> proc.name == it.name } - calledProc?.let { proc -> - proc.params.filter { it.second != ParamDirection.OUT }.mapIndexed { i, (param, _) -> - Assign(cast(param, param.type), cast(it.params[i], param.type)) - } + - proc.params.filter { it.second != ParamDirection.IN }.mapIndexed { i, (param, _) -> - Assign(cast((it.params[i] as RefExpr<*>).decl as VarDecl<*>, param.type), - cast(param.ref, param.type)) - } - } ?: listOf() - } - } - - val allAssignments = (assignments + threadStart + procInvoke) - - val ptrVars = LinkedHashSet>(bases.map { (it as RefExpr<*>).decl as VarDecl<*> }) - var lastPtrVars = emptySet>() - - while (ptrVars != lastPtrVars) { - lastPtrVars = ptrVars.toSet() - - val rhs = allAssignments.filter { ptrVars.contains(it.varDecl) }.map { unboxMod(it.expr) } - allAssignments.filter { - ptrVars.contains(it.varDecl) && (it.expr !is LitExpr<*>) && (it.expr !is RefExpr<*>) - } - ptrVars.addAll(rhs.filterIsInstance(RefExpr::class.java).map { it.decl as VarDecl<*> }) - } - - val lits = LinkedHashMap, MutableSet>>() - val alias = LinkedHashMap, MutableSet>>() - - val litAssignments = allAssignments.filter { - ptrVars.contains(it.varDecl) && unboxMod(it.expr) is LitExpr<*> - }.map { Pair(it.varDecl, unboxMod(it.expr) as LitExpr<*>) } + allAssignments.filter { - ptrVars.contains(it.varDecl) && (unboxMod(it.expr) !is LitExpr<*> && unboxMod(it.expr) !is RefExpr<*>) - }.map { Pair(it.varDecl, MallocLitExpr(it.varDecl.type)) } - litAssignments.forEach { lits.getOrPut(it.first) { LinkedHashSet() }.add(it.second) } - val varAssignments = allAssignments.filter { - ptrVars.contains(it.varDecl) && unboxMod(it.expr) is RefExpr<*> + get() = lazy { + val attempt = + Try.attempt { + fun unboxMod(e: Expr<*>): Expr<*> = if (e is ModExpr<*>) unboxMod(e.ops[0]) else e + + val bases = + this.procedures + .flatMap { + it.edges.flatMap { + it.getFlatLabels().flatMap { it.dereferences.map { unboxMod(it.array) } } + } } - .map { Pair(it.varDecl, (unboxMod(it.expr) as RefExpr<*>).decl as VarDecl<*>) } - varAssignments.forEach { alias.getOrPut(it.first) { LinkedHashSet() }.add(it.second) } - varAssignments.forEach { lits.putIfAbsent(it.first, LinkedHashSet()) } - - var lastLits = emptyMap, MutableSet>>() - while (lastLits != lits) { - lastLits = lits.toMap() - alias.forEach { - lits.getOrPut(it.key) { LinkedHashSet() } - .addAll(it.value.flatMap { lits.getOrDefault(it, emptySet()) }) - } + .filter { it !is LitExpr<*> && it !is Dereference<*, *, *> } + .toSet() + checkState(bases.all { it is RefExpr<*> }) + + // value assignments are either assignments, or thread start statements, or procedure invoke + // statements + val assignments = + this.procedures.flatMap { + it.edges.flatMap { + it + .getFlatLabels() + .filter { it is StmtLabel && it.stmt is AssignStmt<*> } + .map { (it as StmtLabel).stmt as AssignStmt<*> } } - - lits.filter { bases.contains(it.key.ref) } + } + val threadStart = + this.procedures.flatMap { + it.edges + .flatMap { it.getFlatLabels().filterIsInstance() } + .flatMap { + val calledProc = this.procedures.find { proc -> proc.name == it.name } + calledProc?.let { proc -> + proc.params + .withIndex() + .filter { (_, it) -> it.second != ParamDirection.OUT } + .map { (i, pair) -> + val (param, _) = pair + Assign(cast(param, param.type), cast(it.params[i], param.type)) + } + + proc.params + .withIndex() + .filter { (i, pair) -> + pair.second != ParamDirection.IN && it.params[i] is RefExpr<*> + } + .map { (i, pair) -> + val (param, _) = pair + Assign( + cast((it.params[i] as RefExpr<*>).decl as VarDecl<*>, param.type), + cast(param.ref, param.type), + ) + } + } ?: listOf() + } + } + val procInvoke = + this.procedures.flatMap { + it.edges + .flatMap { it.getFlatLabels().filterIsInstance() } + .flatMap { + val calledProc = this.procedures.find { proc -> proc.name == it.name } + calledProc?.let { proc -> + proc.params + .filter { it.second != ParamDirection.OUT } + .mapIndexed { i, (param, _) -> + Assign(cast(param, param.type), cast(it.params[i], param.type)) + } + + proc.params + .filter { it.second != ParamDirection.IN } + .mapIndexed { i, (param, _) -> + Assign( + cast((it.params[i] as RefExpr<*>).decl as VarDecl<*>, param.type), + cast(param.ref, param.type), + ) + } + } ?: listOf() + } + } + + val allAssignments = (assignments + threadStart + procInvoke) + + val ptrVars = LinkedHashSet>(bases.map { (it as RefExpr<*>).decl as VarDecl<*> }) + var lastPtrVars = emptySet>() + + while (ptrVars != lastPtrVars) { + lastPtrVars = ptrVars.toSet() + + val rhs = allAssignments.filter { ptrVars.contains(it.varDecl) }.map { unboxMod(it.expr) } + allAssignments.filter { + ptrVars.contains(it.varDecl) && (it.expr !is LitExpr<*>) && (it.expr !is RefExpr<*>) + } + ptrVars.addAll(rhs.filterIsInstance(RefExpr::class.java).map { it.decl as VarDecl<*> }) } - if (attempt.isSuccess) { - attempt.asSuccess().value - } else { - emptyMap() + + val lits = LinkedHashMap, MutableSet>>() + val alias = LinkedHashMap, MutableSet>>() + + val litAssignments = + allAssignments + .filter { ptrVars.contains(it.varDecl) && unboxMod(it.expr) is LitExpr<*> } + .map { Pair(it.varDecl, unboxMod(it.expr) as LitExpr<*>) } + + allAssignments + .filter { + ptrVars.contains(it.varDecl) && + (unboxMod(it.expr) !is LitExpr<*> && unboxMod(it.expr) !is RefExpr<*>) + } + .map { Pair(it.varDecl, MallocLitExpr(it.varDecl.type)) } + litAssignments.forEach { lits.getOrPut(it.first) { LinkedHashSet() }.add(it.second) } + val varAssignments = + allAssignments + .filter { ptrVars.contains(it.varDecl) && unboxMod(it.expr) is RefExpr<*> } + .map { Pair(it.varDecl, (unboxMod(it.expr) as RefExpr<*>).decl as VarDecl<*>) } + varAssignments.forEach { alias.getOrPut(it.first) { LinkedHashSet() }.add(it.second) } + varAssignments.forEach { lits.putIfAbsent(it.first, LinkedHashSet()) } + + var lastLits = emptyMap, MutableSet>>() + while (lastLits != lits) { + lastLits = lits.toMap() + alias.forEach { + lits + .getOrPut(it.key) { LinkedHashSet() } + .addAll(it.value.flatMap { lits.getOrDefault(it, emptySet()) }) + } } - } -fun Collection>.pointsTo(xcfa: XCFA) = flatMap { xcfa.pointsToGraph[it] ?: emptyList() }.toSet() -fun VarAccessMap.pointsTo(xcfa: XCFA) = keys.pointsTo(xcfa) \ No newline at end of file + lits.filter { bases.contains(it.key.ref) } + } + if (attempt.isSuccess) { + attempt.asSuccess().value + } else { + emptyMap() + } + } + +fun Collection>.pointsTo(xcfa: XCFA) = + flatMap { xcfa.pointsToGraph[it] ?: emptyList() }.toSet() + +fun VarAccessMap.pointsTo(xcfa: XCFA) = keys.pointsTo(xcfa) + +private fun assignStmtLabelOf( + lhs: VarDecl, + rhs: Expr, + metadata: MetaData = EmptyMetaData, +): StmtLabel = StmtLabel(Assign(lhs, rhs), metadata = metadata) + +@Suppress("FunctionName") +fun AssignStmtLabel( + lhs: VarDecl, + rhs: Expr, + metadata: MetaData = EmptyMetaData, +): StmtLabel = assignStmtLabelOf(lhs, cast(rhs, lhs.type), metadata) + +@Suppress("FunctionName") +fun AssignStmtLabel( + lhs: VarDecl, + rhs: Expr, + type: T3, + metadata: MetaData = EmptyMetaData, +): StmtLabel = assignStmtLabelOf(cast(lhs, type), cast(rhs, type), metadata) + +@Suppress("FunctionName") +fun AssignStmtLabel( + lhs: RefExpr, + rhs: Expr, + metadata: MetaData = EmptyMetaData, +): StmtLabel = assignStmtLabelOf(cast(lhs.decl as VarDecl<*>, rhs.type), rhs, metadata) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/XcfaToC.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/XcfaToC.kt index 6eacb8f086..c22f3ce765 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/XcfaToC.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/XcfaToC.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa import hu.bme.mit.theta.core.decl.VarDecl @@ -55,10 +54,15 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cint import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cint.CUnsignedInt import hu.bme.mit.theta.xcfa.model.* -private const val arraySize = 10; +private const val arraySize = 10 -fun XCFA.toC(parseContext: ParseContext, arraySupport: Boolean, exactArraySupport: Boolean, - intRangeConstraint: Boolean): String = """ +fun XCFA.toC( + parseContext: ParseContext, + arraySupport: Boolean, + exactArraySupport: Boolean, + intRangeConstraint: Boolean, +): String = + """ extern void abort(); extern unsigned short __VERIFIER_nondet_ushort(); extern short __VERIFIER_nondet_short(); @@ -150,40 +154,46 @@ fun XCFA.toC(parseContext: ParseContext, arraySupport: Boolean, exactArraySuppor } } -""".trimIndent() - +""" + .trimIndent() fun XcfaProcedure.decl(parseContext: ParseContext): String = - if (params.isNotEmpty()) { - "${CComplexType.getType(params[0].first.ref, parseContext).toC()} ${name.toC()}(${ + if (params.isNotEmpty()) { + "${CComplexType.getType(params[0].first.ref, parseContext).toC()} ${name.toC()}(${ params.subList(1, params.size).joinToString(", ") { it.decl(parseContext) } })" - } else { - "void ${name.toC()}()" - } + } else { + "void ${name.toC()}()" + } private fun VarDecl<*>.unsafeBounds(parseContext: ParseContext) = - CComplexType.getType(ref, parseContext).accept(object : CComplexTypeVisitor?>() { + CComplexType.getType(ref, parseContext) + .accept( + object : CComplexTypeVisitor?>() { override fun visit(type: CInteger, param: Unit): Expr? { - return Or(Leq(ref, Int(-1_000_000_000)), Geq(ref, Int(1_000_000_000))) + return Or(Leq(ref, Int(-1_000_000_000)), Geq(ref, Int(1_000_000_000))) } override fun visit(type: CBool?, param: Unit?): Expr? { - return null + return null } - }, Unit) - -private fun Set>.unsafeBounds(parseContext: ParseContext, intRangeConstraint: Boolean): String { - if (!intRangeConstraint) return "" - - val conditions = this.map { (it.unsafeBounds(parseContext))?.toC(parseContext) }.filterNotNull() - return if (conditions.isNotEmpty()) - "if (" + conditions.joinToString(" || ") + ") abort();" - else - "" + }, + Unit, + ) + +private fun Set>.unsafeBounds( + parseContext: ParseContext, + intRangeConstraint: Boolean, +): String { + if (!intRangeConstraint) return "" + + val conditions = this.map { (it.unsafeBounds(parseContext))?.toC(parseContext) }.filterNotNull() + return if (conditions.isNotEmpty()) "if (" + conditions.joinToString(" || ") + ") abort();" + else "" } -fun XcfaProcedure.def(parseContext: ParseContext, intRangeConstraint: Boolean): String = """ +fun XcfaProcedure.def(parseContext: ParseContext, intRangeConstraint: Boolean): String = + """ ${decl(parseContext)} { // return parameter ${if (params.isNotEmpty()) params[0].decl(parseContext) + ";" else ""} @@ -208,18 +218,22 @@ fun XcfaProcedure.def(parseContext: ParseContext, intRangeConstraint: Boolean): // return expression ${if (params.isNotEmpty()) "return " + params[0].first.name.toC() + ";" else ""} } -""".trimIndent() +""" + .trimIndent() private fun XcfaLocation.toC(parseContext: ParseContext, intRangeConstraint: Boolean): String = - if (this.error) { - "reach_error();" - } else when (outgoingEdges.size) { - 0 -> "goto ${name.toC()};" - 1 -> outgoingEdges.first().getFlatLabels().joinToString("\n") - { it.toC(parseContext, intRangeConstraint) } + " goto ${outgoingEdges.first().target.name.toC()};" - - 2 -> - """ + if (this.error) { + "reach_error();" + } else + when (outgoingEdges.size) { + 0 -> "goto ${name.toC()};" + 1 -> + outgoingEdges.first().getFlatLabels().joinToString("\n") { + it.toC(parseContext, intRangeConstraint) + } + " goto ${outgoingEdges.first().target.name.toC()};" + + 2 -> + """ switch(__VERIFIER_nondet__Bool()) { ${ outgoingEdges.mapIndexed { index, xcfaEdge -> @@ -231,10 +245,11 @@ private fun XcfaLocation.toC(parseContext: ParseContext, intRangeConstraint: Boo } default: abort(); } - """.trimIndent() - - else -> """ + .trimIndent() + + else -> + """ switch(__VERIFIER_nondet_int()) { ${ outgoingEdges.mapIndexed { index, xcfaEdge -> @@ -246,134 +261,139 @@ private fun XcfaLocation.toC(parseContext: ParseContext, intRangeConstraint: Boo } default: abort(); } - """.trimIndent() + """ + .trimIndent() } private fun XcfaLabel.toC(parseContext: ParseContext, intRangeConstraint: Boolean): String = - when (this) { - is StmtLabel -> this.toC(parseContext, intRangeConstraint) - is SequenceLabel -> labels.joinToString("\n") { it.toC(parseContext, intRangeConstraint) } - is InvokeLabel -> "${params[0].toC(parseContext)} = ${name.toC()}(${ + when (this) { + is StmtLabel -> this.toC(parseContext, intRangeConstraint) + is SequenceLabel -> labels.joinToString("\n") { it.toC(parseContext, intRangeConstraint) } + is InvokeLabel -> + "${params[0].toC(parseContext)} = ${name.toC()}(${ params.subList(1, params.size).map { it.toC(parseContext) }.joinToString(", ") });" - else -> TODO("Not yet supported: $this") - } + else -> TODO("Not yet supported: $this") + } private fun StmtLabel.toC(parseContext: ParseContext, intRangeConstraint: Boolean): String = - when (stmt) { - is HavocStmt<*> -> "${stmt.varDecl.name.toC()} = __VERIFIER_nondet_${ + when (stmt) { + is HavocStmt<*> -> + "${stmt.varDecl.name.toC()} = __VERIFIER_nondet_${ CComplexType.getType(stmt.varDecl.ref, parseContext).toC() }(); ${setOf(stmt.varDecl).unsafeBounds(parseContext, intRangeConstraint)}" - is AssignStmt<*> -> "${stmt.varDecl.name.toC()} = ${stmt.expr.toC(parseContext)};" - is MemoryAssignStmt<*, *, *> -> "${stmt.deref.toC(parseContext)} = ${stmt.expr.toC(parseContext)};" - is AssumeStmt -> "if(!${stmt.cond.toC(parseContext)}) abort();" - else -> TODO("Not yet supported: $stmt") - } + is AssignStmt<*> -> "${stmt.varDecl.name.toC()} = ${stmt.expr.toC(parseContext)};" + is MemoryAssignStmt<*, *, *> -> + "${stmt.deref.toC(parseContext)} = ${stmt.expr.toC(parseContext)};" + is AssumeStmt -> "if(!${stmt.cond.toC(parseContext)}) abort();" + else -> TODO("Not yet supported: $stmt") + } fun Pair, ParamDirection>.decl(parseContext: ParseContext): String = -// if(second == ParamDirection.IN) { - first.decl(parseContext) + // if(second == ParamDirection.IN) { + first.decl(parseContext) + // } else error("Only IN params are supported right now") fun VarDecl<*>.decl(parseContext: ParseContext): String = - "${CComplexType.getType(ref, parseContext).toC()} ${name.toC()}" + "${CComplexType.getType(ref, parseContext).toC()} ${name.toC()}" private fun CComplexType.toC(): String = - when (this) { - is CArray -> "${this.embeddedType.toC()}_arr" - is CSignedInt -> "int" - is CUnsignedInt -> "unsigned int" - is CChar -> "char" - is CBool -> "_Bool" - else -> this.typeName - } + when (this) { + is CArray -> "${this.embeddedType.toC()}_arr" + is CSignedInt -> "int" + is CUnsignedInt -> "unsigned int" + is CChar -> "char" + is CBool -> "_Bool" + else -> this.typeName + } // below functions implement the serialization of expressions to C-style expressions fun Expr<*>.toC(parseContext: ParseContext) = - when (this) { - is NullaryExpr<*> -> this.toC(parseContext) - is UnaryExpr<*, *> -> this.toC(parseContext) - is BinaryExpr<*, *> -> this.toC(parseContext) - is MultiaryExpr<*, *> -> this.toC(parseContext) - is ArrayWriteExpr<*, *> -> this.toC(parseContext) - is ArrayReadExpr<*, *> -> this.toC(parseContext) - is Dereference<*, *, *> -> this.toC(parseContext) - is IteExpr<*> -> this.toC(parseContext) - else -> TODO("Not yet supported: $this") - } + when (this) { + is NullaryExpr<*> -> this.toC(parseContext) + is UnaryExpr<*, *> -> this.toC(parseContext) + is BinaryExpr<*, *> -> this.toC(parseContext) + is MultiaryExpr<*, *> -> this.toC(parseContext) + is ArrayWriteExpr<*, *> -> this.toC(parseContext) + is ArrayReadExpr<*, *> -> this.toC(parseContext) + is Dereference<*, *, *> -> this.toC(parseContext) + is IteExpr<*> -> this.toC(parseContext) + else -> TODO("Not yet supported: $this") + } fun Dereference<*, *, *>.toC(parseContext: ParseContext): String = "$array[$offset]" fun ArrayWriteExpr<*, *>.toC(parseContext: ParseContext): String = - "array_write(${this.array.toC(parseContext)}, ${this.index.toC(parseContext)}, ${this.elem.toC(parseContext)})" + "array_write(${this.array.toC(parseContext)}, ${this.index.toC(parseContext)}, ${this.elem.toC(parseContext)})" fun ArrayReadExpr<*, *>.toC(parseContext: ParseContext): String = - "array_read(${this.array.toC(parseContext)}, ${this.index.toC(parseContext)})" + "array_read(${this.array.toC(parseContext)}, ${this.index.toC(parseContext)})" fun IteExpr<*>.toC(parseContext: ParseContext): String = - "(${this.cond.toC(parseContext)} ? ${this.then.toC(parseContext)} : ${this.`else`.toC(parseContext)})" + "(${this.cond.toC(parseContext)} ? ${this.then.toC(parseContext)} : ${this.`else`.toC(parseContext)})" // nullary: ref + lit fun NullaryExpr<*>.toC(parseContext: ParseContext): String = - when (this) { - is RefExpr<*> -> this.decl.name.toC() - is LitExpr<*> -> (this as LitExpr<*>).toC(parseContext) - else -> TODO("Not yet supported: $this") - } + when (this) { + is RefExpr<*> -> this.decl.name.toC() + is LitExpr<*> -> (this as LitExpr<*>).toC(parseContext) + else -> TODO("Not yet supported: $this") + } fun LitExpr<*>.toC(parseContext: ParseContext): String = - when (this) { - is FalseExpr -> "0" - is TrueExpr -> "1" - is IntLitExpr -> this.value.toString() - is RatLitExpr -> "(${this.num}.0/${this.denom}.0)" - is FpLitExpr -> FpUtils.fpLitExprToBigFloat(FpRoundingMode.RNE, this).toString() - is BvLitExpr -> BvUtils.neutralBvLitExprToBigInteger(this).toString() - else -> error("Not supported: $this") - } - + when (this) { + is FalseExpr -> "0" + is TrueExpr -> "1" + is IntLitExpr -> this.value.toString() + is RatLitExpr -> "(${this.num}.0/${this.denom}.0)" + is FpLitExpr -> FpUtils.fpLitExprToBigFloat(FpRoundingMode.RNE, this).toString() + is BvLitExpr -> BvUtils.neutralBvLitExprToBigInteger(this).toString() + else -> error("Not supported: $this") + } fun UnaryExpr<*, *>.toC(parseContext: ParseContext): String = - "(${this.cOperator()} ${op.toC(parseContext)})" + "(${this.cOperator()} ${op.toC(parseContext)})" fun BinaryExpr<*, *>.toC(parseContext: ParseContext): String = - if (leftOp.type is ArrayType<*, *>) { - "${this.arrayCOperator()}(${leftOp.toC(parseContext)}, ${rightOp.toC(parseContext)})" - } else if (this is ModExpr<*>) { - "( (${leftOp.toC(parseContext)} % ${rightOp.toC(parseContext)} + ${rightOp.toC(parseContext)}) % ${ + if (leftOp.type is ArrayType<*, *>) { + "${this.arrayCOperator()}(${leftOp.toC(parseContext)}, ${rightOp.toC(parseContext)})" + } else if (this is ModExpr<*>) { + "( (${leftOp.toC(parseContext)} % ${rightOp.toC(parseContext)} + ${rightOp.toC(parseContext)}) % ${ rightOp.toC(parseContext) } )" - } else { - "(${leftOp.toC(parseContext)} ${this.cOperator()} ${rightOp.toC(parseContext)})" - } + } else { + "(${leftOp.toC(parseContext)} ${this.cOperator()} ${rightOp.toC(parseContext)})" + } fun MultiaryExpr<*, *>.toC(parseContext: ParseContext): String = - ops.joinToString(separator = " ${this.cOperator()} ", prefix = "(", postfix = ")") { it.toC(parseContext) } + ops.joinToString(separator = " ${this.cOperator()} ", prefix = "(", postfix = ")") { + it.toC(parseContext) + } fun Expr<*>.cOperator() = - when (this) { - is EqExpr<*> -> "==" - is NeqExpr<*> -> "!=" - is OrExpr -> "||" - is AndExpr -> "&&" - is NotExpr -> "!" - // is ModExpr<*> -> "%" // handled above - is DivExpr<*> -> "/" - - is UnaryExpr<*, *> -> operatorLabel - is BinaryExpr<*, *> -> operatorLabel - is MultiaryExpr<*, *> -> operatorLabel - else -> TODO("Not yet implemented operator label for expr: $this") - } + when (this) { + is EqExpr<*> -> "==" + is NeqExpr<*> -> "!=" + is OrExpr -> "||" + is AndExpr -> "&&" + is NotExpr -> "!" + // is ModExpr<*> -> "%" // handled above + is DivExpr<*> -> "/" + + is UnaryExpr<*, *> -> operatorLabel + is BinaryExpr<*, *> -> operatorLabel + is MultiaryExpr<*, *> -> operatorLabel + else -> TODO("Not yet implemented operator label for expr: $this") + } fun Expr<*>.arrayCOperator() = - when (this) { - is EqExpr<*> -> "array_equals" - is NeqExpr<*> -> "!array_equals" - else -> TODO("Not yet implemented array operator label for expr: $this") - } + when (this) { + is EqExpr<*> -> "array_equals" + is NeqExpr<*> -> "!array_equals" + else -> TODO("Not yet implemented array operator label for expr: $this") + } -fun String.toC() = - this.replace(Regex("[^A-Za-z_0-9]"), "_") \ No newline at end of file +fun String.toC() = this.replace(Regex("[^A-Za-z_0-9]"), "_") diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaLabelAdapter.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaLabelAdapter.kt index eeebe05881..a9ef886081 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaLabelAdapter.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaLabelAdapter.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.gson import com.google.gson.Gson @@ -31,69 +30,69 @@ import kotlin.reflect.full.companionObject import kotlin.reflect.full.functions class XcfaLabelAdapter(val scope: Scope, val env: Env, val gsonSupplier: () -> Gson) : - TypeAdapter() { + TypeAdapter() { - private lateinit var gson: Gson + private lateinit var gson: Gson - override fun write(writer: JsonWriter, value: XcfaLabel) { - initGson() - writer.beginObject() - writer.name("type").value(value.javaClass.name) - writer.name("metadata") - gson.toJson(gson.toJsonTree(value.metadata), writer) - if (value is SequenceLabel || value is NondetLabel) { - writer.name("labels") - writer.beginArray() - val labels = if (value is SequenceLabel) value.labels else (value as NondetLabel).labels - labels.forEach { write(writer, it) } - writer.endArray() - } else { - writer.name("content").value(value.toString()) - } - writer.endObject() + override fun write(writer: JsonWriter, value: XcfaLabel) { + initGson() + writer.beginObject() + writer.name("type").value(value.javaClass.name) + writer.name("metadata") + gson.toJson(gson.toJsonTree(value.metadata), writer) + if (value is SequenceLabel || value is NondetLabel) { + writer.name("labels") + writer.beginArray() + val labels = if (value is SequenceLabel) value.labels else (value as NondetLabel).labels + labels.forEach { write(writer, it) } + writer.endArray() + } else { + writer.name("content").value(value.toString()) } + writer.endObject() + } - override fun read(reader: JsonReader): XcfaLabel { - initGson() - reader.beginObject() - check(reader.nextName() == "type") - val typeName = reader.nextString() - val clazz = Class.forName(typeName).kotlin - check(reader.nextName() == "metadata") - val metadata: MetaData = gson.fromJson(reader, MetaData::class.java) - if (clazz == SequenceLabel::class || clazz == NondetLabel::class) { - check(reader.nextName() == "labels") - reader.beginArray() - val labels = ArrayList() - while (reader.peek() != JsonToken.END_ARRAY) { - labels.add(read(reader)) - } - reader.endArray() - reader.endObject() - val constr = clazz.constructors.find { it.parameters.size == 2 } - checkNotNull(constr) - return constr.call(labels, metadata) as XcfaLabel - } else { - check(reader.nextName() == "content") - val content = reader.nextString() - val constructor = clazz.companionObject?.functions?.find { it.name == "fromString" } - checkNotNull(constructor) { "${clazz.simpleName} has no fromString() method." } - val obj = - try { - constructor.call(clazz.companionObject!!.objectInstance, content, scope, env, - metadata) - } catch (e: Exception) { - System.err.println( - "Could not parse $content\nscope: ${scope}\nenv: $env\ntype: ${clazz.simpleName}") - throw e - } - check(obj is XcfaLabel) - reader.endObject() - return obj + override fun read(reader: JsonReader): XcfaLabel { + initGson() + reader.beginObject() + check(reader.nextName() == "type") + val typeName = reader.nextString() + val clazz = Class.forName(typeName).kotlin + check(reader.nextName() == "metadata") + val metadata: MetaData = gson.fromJson(reader, MetaData::class.java) + if (clazz == SequenceLabel::class || clazz == NondetLabel::class) { + check(reader.nextName() == "labels") + reader.beginArray() + val labels = ArrayList() + while (reader.peek() != JsonToken.END_ARRAY) { + labels.add(read(reader)) + } + reader.endArray() + reader.endObject() + val constr = clazz.constructors.find { it.parameters.size == 2 } + checkNotNull(constr) + return constr.call(labels, metadata) as XcfaLabel + } else { + check(reader.nextName() == "content") + val content = reader.nextString() + val constructor = clazz.companionObject?.functions?.find { it.name == "fromString" } + checkNotNull(constructor) { "${clazz.simpleName} has no fromString() method." } + val obj = + try { + constructor.call(clazz.companionObject!!.objectInstance, content, scope, env, metadata) + } catch (e: Exception) { + System.err.println( + "Could not parse $content\nscope: ${scope}\nenv: $env\ntype: ${clazz.simpleName}" + ) + throw e } + check(obj is XcfaLabel) + reader.endObject() + return obj } + } - private fun initGson() { - if (!this::gson.isInitialized) gson = gsonSupplier() - } -} \ No newline at end of file + private fun initGson() { + if (!this::gson.isInitialized) gson = gsonSupplier() + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Visualizer.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Visualizer.kt index 177c75758d..695b17e69b 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Visualizer.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Visualizer.kt @@ -13,31 +13,67 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.model -fun XCFA.toDot(edgeLabelCustomizer: ((XcfaEdge) -> String)? = null): String { - val builder = StringBuilder() - builder.appendLine("digraph G {") - builder.appendLine("label=\"$name\";") +typealias LabelCustomizer = (XcfaEdge) -> String + +fun XCFA.toDot(edgeLabelCustomizer: LabelCustomizer? = null): String = + xcfaToDot(name, procedures.map { DottableProcedure(it) }, edgeLabelCustomizer) + +fun XcfaProcedure.toDot(edgeLabelCustomizer: LabelCustomizer? = null): String = + xcfaProcedureToDot(name, locs, edges, edgeLabelCustomizer) + +@Suppress("unused") +fun XcfaBuilder.toDot(edgeLabelCustomizer: LabelCustomizer? = null): String = + xcfaToDot(name, getProcedures().map { DottableProcedure(it) }, edgeLabelCustomizer) + +fun XcfaProcedureBuilder.toDot(edgeLabelCustomizer: LabelCustomizer? = null): String = + xcfaProcedureToDot(name, getLocs(), getEdges(), edgeLabelCustomizer) + +private class DottableProcedure( + private val procedure: XcfaProcedure?, + private val procedureBuilder: XcfaProcedureBuilder?, +) { + constructor(procedure: XcfaProcedure) : this(procedure, null) - for ((i, procedure) in procedures.withIndex()) { - builder.appendLine("subgraph cluster_$i {") - builder.appendLine(procedure.toDot(edgeLabelCustomizer)) - builder.appendLine("}") - } + constructor(procedureBuilder: XcfaProcedureBuilder) : this(null, procedureBuilder) + fun toDot(edgeLabelCustomizer: LabelCustomizer? = null): String = + procedure?.toDot(edgeLabelCustomizer) ?: procedureBuilder!!.toDot(edgeLabelCustomizer) +} + +private fun xcfaToDot( + name: String, + procedures: List, + edgeLabelCustomizer: LabelCustomizer? = null, +): String { + val builder = StringBuilder() + builder.appendLine("digraph G {") + builder.appendLine("label=\"$name\";") + + for ((i, procedure) in procedures.withIndex()) { + builder.appendLine("subgraph cluster_$i {") + builder.appendLine(procedure.toDot(edgeLabelCustomizer)) builder.appendLine("}") - return builder.toString() + } + + builder.appendLine("}") + return builder.toString() } -fun XcfaProcedure.toDot(edgeLabelCustomizer: ((XcfaEdge) -> String)?): String { - val builder = StringBuilder() - builder.appendLine("label=\"$name\";") - locs.forEach { builder.appendLine("${it.name}[];") } - edges.forEach { - builder.appendLine( - "${it.source.name} -> ${it.target.name} [label=\"${it.label} ${edgeLabelCustomizer?.invoke(it) ?: ""}\"];") - } - return builder.toString() -} \ No newline at end of file +private fun xcfaProcedureToDot( + name: String, + locs: Set, + edges: Set, + edgeLabelCustomizer: LabelCustomizer? = null, +): String { + val builder = StringBuilder() + builder.appendLine("label=\"$name\";") + locs.forEach { builder.appendLine("${it.name}[];") } + edges.forEach { + builder.appendLine( + "${it.source.name} -> ${it.target.name} [label=\"${it.label} ${edgeLabelCustomizer?.invoke(it) ?: ""}\"];" + ) + } + return builder.toString() +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AssumeFalseRemovalPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AssumeFalseRemovalPass.kt index 17bbdde26e..83fd73602b 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AssumeFalseRemovalPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AssumeFalseRemovalPass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.stmt.AssumeStmt @@ -21,30 +20,31 @@ import hu.bme.mit.theta.core.type.booltype.FalseExpr import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* -/** - * Removes assume(false) statements and any consequently unreachable edges and locations. - */ +/** Removes assume(false) statements and any consequently unreachable edges and locations. */ class AssumeFalseRemovalPass : ProcedurePass { - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - builder.getEdges().toSet().forEach { edge -> - if (edge.getFlatLabels() - .any { it is StmtLabel && it.stmt is AssumeStmt && it.stmt.cond is FalseExpr }) { - builder.removeEdge(edge) - } + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + builder.getEdges().toSet().forEach { edge -> + if ( + edge.getFlatLabels().any { + it is StmtLabel && it.stmt is AssumeStmt && it.stmt.cond is FalseExpr } + ) { + builder.removeEdge(edge) + } + } - val getUnreachable: () -> List = { - builder.getLocs().filter { it.incomingEdges.isEmpty() && !it.initial } - } - var unreachable = getUnreachable() - while (unreachable.isNotEmpty()) { - unreachable.forEach { loc -> - loc.outgoingEdges.forEach { builder.removeEdge(it) } - builder.removeLoc(loc) - } - unreachable = getUnreachable() - } - return builder + val getUnreachable: () -> List = { + builder.getLocs().filter { it.incomingEdges.isEmpty() && !it.initial } + } + var unreachable = getUnreachable() + while (unreachable.isNotEmpty()) { + unreachable.forEach { loc -> + loc.outgoingEdges.forEach { builder.removeEdge(it) } + builder.removeLoc(loc) + } + unreachable = getUnreachable() } + return builder + } } diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AtomicReadsOneWritePass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AtomicReadsOneWritePass.kt index d4927ebaad..991f5bd343 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AtomicReadsOneWritePass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AtomicReadsOneWritePass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.Decl @@ -32,151 +31,174 @@ import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.model.* /** - * One atomic unit (atomic block or edge) can have at most one write operation on a global variable and no read can - * happen after writing a global variable. Therefore, each atomic unit that violates this rule is transformed so that - * each global variable is replaced with a local variable that is assigned the value of the global variable at the - * beginning of the atomic unit and the global variable is assigned the value of the local variable at the end of the - * atomic unit. + * One atomic unit (atomic block or edge) can have at most one write operation on a global variable + * and no read can happen after writing a global variable. Therefore, each atomic unit that violates + * this rule is transformed so that each global variable is replaced with a local variable that is + * assigned the value of the global variable at the beginning of the atomic unit and the global + * variable is assigned the value of the local variable at the end of the atomic unit. */ class AtomicReadsOneWritePass : ProcedurePass { - private lateinit var builder: XcfaProcedureBuilder - - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - var indexing: VarIndexing = VarIndexingFactory.indexing(0) - this.builder = builder - - builder.getEdges().toSet().forEach { edge -> - if (edge.getFlatLabels().any { it.isAtomicBegin }) { - val toReplace = edge.countAtomicBlockAccesses() - .mapNotNull { (v, ao) -> if (ao.wrongWrite) v else null } - if (toReplace.isNotEmpty()) { - val localVersions = toReplace.associateWith { v -> - indexing = indexing.inc(v) - v.localVersion(indexing) - } - - val newStartEdge = edge.replaceAccesses(localVersions) - val initialAssigns = SequenceLabel(localVersions.map { (v, local) -> - StmtLabel(AssignStmt.of(cast(local, local.type), cast(v.ref, local.type))) - }) - val atomicBeginIndex = newStartEdge.getFlatLabels().indexOfFirst { it.isAtomicBegin } - val newLabels = newStartEdge.getFlatLabels().toMutableList().apply { - add(atomicBeginIndex + 1, initialAssigns) - } - builder.removeEdge(newStartEdge) - builder.addEdge(newStartEdge.withLabel(SequenceLabel(newLabels))) - } + private lateinit var builder: XcfaProcedureBuilder + + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + var indexing: VarIndexing = VarIndexingFactory.indexing(0) + this.builder = builder + + builder.getEdges().toSet().forEach { edge -> + if (edge.getFlatLabels().any { it.isAtomicBegin }) { + val toReplace = + edge.countAtomicBlockAccesses().mapNotNull { (v, ao) -> if (ao.wrongWrite) v else null } + if (toReplace.isNotEmpty()) { + val localVersions = + toReplace.associateWith { v -> + indexing = indexing.inc(v) + v.localVersion(indexing) } - } - builder.getEdges().toSet().forEach { edge -> - val toReplace = edge.countEdgeAccesses() - .mapNotNull { (v, ao) -> if (ao.wrongWrite) v else null } - if (toReplace.isNotEmpty()) { - val localVersions = toReplace.associateWith { v -> - indexing = indexing.inc(v) - v.localVersion(indexing) - } - val initialAssigns = localVersions.map { (v, local) -> - StmtLabel(AssignStmt.of(cast(local, local.type), cast(v.ref, local.type))) - } - val newLabels = edge.getFlatLabels().map { it.replaceAccesses(localVersions) } - val finalAssigns = localVersions.map { (v, local) -> - StmtLabel(AssignStmt.of(cast(v, local.type), cast(local.ref, v.type))) - } - builder.removeEdge(edge) - builder.addEdge(edge.withLabel(SequenceLabel(initialAssigns + newLabels + finalAssigns))) + val newStartEdge = edge.replaceAccesses(localVersions) + val initialAssigns = + SequenceLabel( + localVersions.map { (v, local) -> + StmtLabel(AssignStmt.of(cast(local, local.type), cast(v.ref, local.type))) + } + ) + val atomicBeginIndex = newStartEdge.getFlatLabels().indexOfFirst { it.isAtomicBegin } + val newLabels = + newStartEdge.getFlatLabels().toMutableList().apply { + add(atomicBeginIndex + 1, initialAssigns) } + builder.removeEdge(newStartEdge) + builder.addEdge(newStartEdge.withLabel(SequenceLabel(newLabels))) } - return builder + } } - private fun VarDecl.localVersion(indexing: VarIndexing): VarDecl = - Decls.Var("${name}_l${indexing.get(this)}", type) - - private data class AccessOrder(var write: Boolean = false, var wrongWrite: Boolean = false) - - private fun XcfaEdge.countAtomicBlockAccesses(): Map, AccessOrder> { - val accesses = mutableMapOf, AccessOrder>() // over-approximation of writePrecedesRead - val toVisit = mutableListOf(this) - while (toVisit.isNotEmpty()) { - val current = toVisit.removeAt(0) - var atomicEnd = false - current.getFlatLabels().forEach { - it.countAccesses(accesses) - atomicEnd = atomicEnd || it.isAtomicEnd - } - if (!atomicEnd) toVisit.addAll(current.target.outgoingEdges) - } - - return accesses + builder.getEdges().toSet().forEach { edge -> + val toReplace = + edge.countEdgeAccesses().mapNotNull { (v, ao) -> if (ao.wrongWrite) v else null } + if (toReplace.isNotEmpty()) { + val localVersions = + toReplace.associateWith { v -> + indexing = indexing.inc(v) + v.localVersion(indexing) + } + val initialAssigns = + localVersions.map { (v, local) -> + StmtLabel(AssignStmt.of(cast(local, local.type), cast(v.ref, local.type))) + } + val newLabels = edge.getFlatLabels().map { it.replaceAccesses(localVersions) } + val finalAssigns = + localVersions.map { (v, local) -> + StmtLabel(AssignStmt.of(cast(v, local.type), cast(local.ref, v.type))) + } + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(SequenceLabel(initialAssigns + newLabels + finalAssigns))) + } } - - private fun XcfaEdge.countEdgeAccesses(): Map, AccessOrder> { - val accesses = mutableMapOf, AccessOrder>() - getFlatLabels().forEach { it.countAccesses(accesses) } - return accesses + return builder + } + + private fun VarDecl.localVersion(indexing: VarIndexing): VarDecl = + Decls.Var("${name}_l${indexing.get(this)}", type) + + private data class AccessOrder(var write: Boolean = false, var wrongWrite: Boolean = false) + + private fun XcfaEdge.countAtomicBlockAccesses(): Map, AccessOrder> { + val accesses = + mutableMapOf, AccessOrder>() // over-approximation of writePrecedesRead + val toVisit = mutableListOf(this) + while (toVisit.isNotEmpty()) { + val current = toVisit.removeAt(0) + var atomicEnd = false + current.getFlatLabels().forEach { + it.countAccesses(accesses) + atomicEnd = atomicEnd || it.isAtomicEnd + } + if (!atomicEnd) toVisit.addAll(current.target.outgoingEdges) } - private fun XcfaLabel.countAccesses(accesses: MutableMap, AccessOrder>) { - collectVarsWithAccessType().filter { (v, _) -> v !in builder.getVars() }.forEach { (v, at) -> - val t = accesses.getOrDefault(v, AccessOrder()) - if (t.write) t.wrongWrite = true - if (at.isWritten) t.write = true - accesses[v] = t - } - } - - private fun XcfaEdge.replaceAccesses(localVersions: Map, VarDecl<*>>): XcfaEdge { - val toVisit = mutableListOf(this) - var first: XcfaEdge? = null - while (toVisit.isNotEmpty()) { - val current = toVisit.removeAt(0) - var atomicEnd = false - val newLabels = current.getFlatLabels().map { - atomicEnd = atomicEnd || it.isAtomicEnd - if (atomicEnd) it else it.replaceAccesses(localVersions) - }.toMutableList() - builder.removeEdge(current) - - if (!atomicEnd) { - toVisit.addAll(current.target.outgoingEdges) - } else { - val atomicEndIndex = newLabels.indexOfFirst { it.isAtomicEnd } - val finalAssigns = SequenceLabel(localVersions.map { (v, local) -> - StmtLabel(AssignStmt.of(cast(v, local.type), cast(local.ref, v.type))) - }) - newLabels.add(atomicEndIndex, finalAssigns) + return accesses + } + + private fun XcfaEdge.countEdgeAccesses(): Map, AccessOrder> { + val accesses = mutableMapOf, AccessOrder>() + getFlatLabels().forEach { it.countAccesses(accesses) } + return accesses + } + + private fun XcfaLabel.countAccesses(accesses: MutableMap, AccessOrder>) { + collectVarsWithAccessType() + .filter { (v, _) -> v !in builder.getVars() } + .forEach { (v, at) -> + val t = accesses.getOrDefault(v, AccessOrder()) + if (t.write) t.wrongWrite = true + if (at.isWritten) t.write = true + accesses[v] = t + } + } + + private fun XcfaEdge.replaceAccesses(localVersions: Map, VarDecl<*>>): XcfaEdge { + val toVisit = mutableListOf(this) + var first: XcfaEdge? = null + while (toVisit.isNotEmpty()) { + val current = toVisit.removeAt(0) + var atomicEnd = false + val newLabels = + current + .getFlatLabels() + .map { + atomicEnd = atomicEnd || it.isAtomicEnd + if (atomicEnd) it else it.replaceAccesses(localVersions) + } + .toMutableList() + builder.removeEdge(current) + + if (!atomicEnd) { + toVisit.addAll(current.target.outgoingEdges) + } else { + val atomicEndIndex = newLabels.indexOfFirst { it.isAtomicEnd } + val finalAssigns = + SequenceLabel( + localVersions.map { (v, local) -> + StmtLabel(AssignStmt.of(cast(v, local.type), cast(local.ref, v.type))) } + ) + newLabels.add(atomicEndIndex, finalAssigns) + } - val newEdge = current.withLabel(SequenceLabel(newLabels)) - builder.addEdge(newEdge) - first = first ?: newEdge - } - return first!! + val newEdge = current.withLabel(SequenceLabel(newLabels)) + builder.addEdge(newEdge) + first = first ?: newEdge } - - private fun XcfaLabel.replaceAccesses(localVersions: Map, VarDecl<*>>): XcfaLabel { - return when (this) { - is StmtLabel -> when (val stmt = stmt) { - is AssignStmt<*> -> StmtLabel(AssignStmt.of( - cast(localVersions[stmt.varDecl] ?: stmt.varDecl, stmt.varDecl.type), - cast(stmt.expr.replace(localVersions), stmt.varDecl.type))) - - is AssumeStmt -> StmtLabel(AssumeStmt.of(stmt.cond.replace(localVersions))) - is HavocStmt<*> -> StmtLabel(HavocStmt.of(localVersions[stmt.varDecl] ?: stmt.varDecl)) - else -> this - } - - is SequenceLabel -> SequenceLabel(labels.map { it.replaceAccesses(localVersions) }, metadata) - is FenceLabel -> this - is NopLabel -> this - else -> error("Unsupported label type at global var atomic localization: $this") + return first!! + } + + private fun XcfaLabel.replaceAccesses(localVersions: Map, VarDecl<*>>): XcfaLabel { + return when (this) { + is StmtLabel -> + when (val stmt = stmt) { + is AssignStmt<*> -> + StmtLabel( + AssignStmt.of( + cast(localVersions[stmt.varDecl] ?: stmt.varDecl, stmt.varDecl.type), + cast(stmt.expr.replace(localVersions), stmt.varDecl.type), + ) + ) + + is AssumeStmt -> StmtLabel(AssumeStmt.of(stmt.cond.replace(localVersions))) + is HavocStmt<*> -> StmtLabel(HavocStmt.of(localVersions[stmt.varDecl] ?: stmt.varDecl)) + else -> this } + + is SequenceLabel -> SequenceLabel(labels.map { it.replaceAccesses(localVersions) }, metadata) + is FenceLabel -> this + is NopLabel -> this + else -> error("Unsupported label type at global var atomic localization: $this") } + } - private fun Expr.replace(localVersions: Map, VarDecl<*>>): Expr = - if (this is RefExpr) localVersions[decl]?.ref?.let { cast(it, decl.type) } ?: this ?: this - else map { it.replace(localVersions) } + private fun Expr.replace(localVersions: Map, VarDecl<*>>): Expr = + if (this is RefExpr) localVersions[decl]?.ref?.let { cast(it, decl.type) } ?: this ?: this + else map { it.replace(localVersions) } } diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/CLibraryFunctionsPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/CLibraryFunctionsPass.kt index 7803f1daaf..0952ecb986 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/CLibraryFunctionsPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/CLibraryFunctionsPass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.VarDecl @@ -21,6 +20,7 @@ import hu.bme.mit.theta.core.type.Type import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.anytype.Reference import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.xcfa.AssignStmtLabel import hu.bme.mit.theta.xcfa.model.* /** @@ -29,109 +29,138 @@ import hu.bme.mit.theta.xcfa.model.* */ class CLibraryFunctionsPass : ProcedurePass { - private val supportedFunctions = setOf( - "printf", - "pthread_join", - "pthread_create", - "pthread_mutex_lock", - "pthread_mutex_unlock", - "pthread_cond_wait", - "pthread_cond_signal", - "pthread_mutex_init", - "pthread_cond_init" + private val supportedFunctions = + setOf( + "printf", + "pthread_join", + "pthread_create", + "pthread_mutex_lock", + "pthread_mutex_unlock", + "pthread_cond_wait", + "pthread_cond_signal", + "pthread_mutex_init", + "pthread_cond_init", + "pthread_exit", ) - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - checkNotNull(builder.metaData["deterministic"]) - for (edge in ArrayList(builder.getEdges())) { - val edges = edge.splitIf(this::predicate) - if (edges.size > 1 || (edges.size == 1 && predicate((edges[0].label as SequenceLabel).labels[0]))) { - builder.removeEdge(edge) - edges.forEach { - if (predicate((it.label as SequenceLabel).labels[0])) { - val invokeLabel = it.label.labels[0] as InvokeLabel - val metadata = invokeLabel.metadata - val labels: List = when (invokeLabel.name) { - "printf" -> listOf(NopLabel) - "pthread_join" -> { - var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.expr - check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) - - listOf(JoinLabel((handle as RefExpr).decl as VarDecl<*>, metadata)) - } - - "pthread_create" -> { - var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.expr - check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) - - val funcptr = invokeLabel.params[3] - check(funcptr is RefExpr && (funcptr as RefExpr).decl is VarDecl) - - val param = invokeLabel.params[4] - - listOf(StartLabel((funcptr as RefExpr).decl.name, - listOf(Int(0), param), // int(0) to solve StartLabel not handling return params - (handle as RefExpr).decl as VarDecl<*>, metadata)) - } - - "pthread_mutex_lock" -> { - var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.expr - check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) - - listOf(FenceLabel(setOf("mutex_lock(${handle.decl.name})"), metadata)) - } - - "pthread_mutex_unlock" -> { - var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.expr - check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) - - listOf(FenceLabel(setOf("mutex_unlock(${handle.decl.name})"), metadata)) - } - - "pthread_cond_wait" -> { - var cond = invokeLabel.params[1] - while (cond is Reference<*, *>) cond = cond.expr - var handle = invokeLabel.params[2] - while (handle is Reference<*, *>) handle = handle.expr - check(cond is RefExpr && (cond as RefExpr).decl is VarDecl) - check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) - - listOf( - FenceLabel(setOf("start_cond_wait(${cond.decl.name},${handle.decl.name})"), - metadata), - FenceLabel(setOf("cond_wait(${cond.decl.name},${handle.decl.name})"), metadata) - ) - } - - "pthread_cond_signal" -> { - var cond = invokeLabel.params[1] - while (cond is Reference<*, *>) cond = cond.expr - check(cond is RefExpr && (cond as RefExpr).decl is VarDecl) - - listOf(FenceLabel(setOf("cond_signal(${cond.decl.name})"), metadata)) - } - - "pthread_mutex_init", "pthread_cond_init" -> listOf(NopLabel) - - else -> error("Unsupported library function ${invokeLabel.name}") - } - edge.withLabel(SequenceLabel(labels)).splitIf { label -> - label is FenceLabel && label.labels.any { l -> l.startsWith("start_cond_wait") } - }.forEach(builder::addEdge) - } else { - builder.addEdge(edge.withLabel(SequenceLabel(it.label.labels))) - } + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + checkNotNull(builder.metaData["deterministic"]) + for (edge in ArrayList(builder.getEdges())) { + val edges = edge.splitIf(this::predicate) + if ( + edges.size > 1 || + (edges.size == 1 && predicate((edges[0].label as SequenceLabel).labels[0])) + ) { + builder.removeEdge(edge) + edges.forEach { + if (predicate((it.label as SequenceLabel).labels[0])) { + val invokeLabel = it.label.labels[0] as InvokeLabel + val metadata = invokeLabel.metadata + var target = it.target + val labels: List = + when (invokeLabel.name) { + "printf" -> listOf(NopLabel) + "pthread_join" -> { + var handle = invokeLabel.params[1] + while (handle is Reference<*, *>) handle = handle.expr + check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) + + listOf( + JoinLabel((handle as RefExpr).decl as VarDecl<*>, metadata), + AssignStmtLabel(invokeLabel.params[0] as RefExpr<*>, Int(0), metadata), + ) + } + + "pthread_create" -> { + var handle = invokeLabel.params[1] + while (handle is Reference<*, *>) handle = handle.expr + check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) + + val funcptr = invokeLabel.params[3] + check(funcptr is RefExpr && (funcptr as RefExpr).decl is VarDecl) + + val param = invokeLabel.params[4] + + listOf( + StartLabel( + (funcptr as RefExpr).decl.name, + listOf( + Int(0), + param, + ), // int(0) to solve StartLabel not handling return params + (handle as RefExpr).decl as VarDecl<*>, + metadata, + ), + AssignStmtLabel(invokeLabel.params[0] as RefExpr<*>, Int(0), metadata), + ) + } + + "pthread_mutex_lock" -> { + var handle = invokeLabel.params[1] + while (handle is Reference<*, *>) handle = handle.expr + check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) + + listOf(FenceLabel(setOf("mutex_lock(${handle.decl.name})"), metadata)) } - } + + "pthread_mutex_unlock" -> { + var handle = invokeLabel.params[1] + while (handle is Reference<*, *>) handle = handle.expr + check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) + + listOf(FenceLabel(setOf("mutex_unlock(${handle.decl.name})"), metadata)) + } + + "pthread_cond_wait" -> { + var cond = invokeLabel.params[1] + while (cond is Reference<*, *>) cond = cond.expr + var handle = invokeLabel.params[2] + while (handle is Reference<*, *>) handle = handle.expr + check(cond is RefExpr && (cond as RefExpr).decl is VarDecl) + check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) + + listOf( + FenceLabel( + setOf("start_cond_wait(${cond.decl.name},${handle.decl.name})"), + metadata, + ), + FenceLabel(setOf("cond_wait(${cond.decl.name},${handle.decl.name})"), metadata), + ) + } + + "pthread_cond_signal" -> { + var cond = invokeLabel.params[1] + while (cond is Reference<*, *>) cond = cond.expr + check(cond is RefExpr && (cond as RefExpr).decl is VarDecl) + + listOf(FenceLabel(setOf("cond_signal(${cond.decl.name})"), metadata)) + } + + "pthread_mutex_init", + "pthread_cond_init" -> listOf(NopLabel) + + "pthread_exit" -> { + target = builder.finalLoc.get() + listOf(FenceLabel(setOf("pthread_exit"), metadata)) + } + + else -> error("Unsupported library function ${invokeLabel.name}") + } + XcfaEdge(it.source, target, SequenceLabel(labels), metadata) + .splitIf { label -> + label is FenceLabel && label.labels.any { l -> l.startsWith("start_cond_wait") } + } + .forEach(builder::addEdge) + } else { + builder.addEdge(it.withLabel(SequenceLabel(it.label.labels))) + } } - return builder + } } + return builder + } - private fun predicate(it: XcfaLabel): Boolean { - return it is InvokeLabel && it.name in supportedFunctions - } -} \ No newline at end of file + private fun predicate(it: XcfaLabel): Boolean { + return it is InvokeLabel && it.name in supportedFunctions + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FetchExecuteWriteback.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FetchExecuteWriteback.kt index 0ed1eee7a1..0bd2e6c4ad 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FetchExecuteWriteback.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FetchExecuteWriteback.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.Decls.Var @@ -39,102 +38,143 @@ import hu.bme.mit.theta.xcfa.model.* */ class FetchExecuteWriteback(val parseContext: ParseContext) : ProcedurePass { - companion object { - - var enabled = false + companion object { - private var cnt = 0 - get() = field++ - } + var enabled = false - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - if (!enabled) return builder - checkNotNull(builder.metaData["deterministic"]) - val localDerefs = builder.getEdges().flatMap { it.getFlatLabels().flatMap { it.dereferences } } + private var cnt = 0 + get() = field++ + } - if (localDerefs.isNotEmpty()) { - val edges = builder.getEdges().filter { it.label.dereferences.isNotEmpty() }.toSet() - for (edge in edges) { - builder.removeEdge(edge) - builder.addEdge(edge.withLabel(edge.label.replaceDerefs(builder))) - } + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + if (!enabled) return builder + checkNotNull(builder.metaData["deterministic"]) + val localDerefs = builder.getEdges().flatMap { it.getFlatLabels().flatMap { it.dereferences } } - return DeterministicPass().run(NormalizePass().run(builder)) - } + if (localDerefs.isNotEmpty()) { + val edges = builder.getEdges().filter { it.label.dereferences.isNotEmpty() }.toSet() + for (edge in edges) { + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(edge.label.replaceDerefs(builder))) + } - return builder + return DeterministicPass().run(NormalizePass().run(builder)) } - private fun XcfaLabel.replaceDerefs(builder: XcfaProcedureBuilder): XcfaLabel = - if (dereferences.isNotEmpty()) { - when (this) { - is NondetLabel -> NondetLabel(labels.map { it.replaceDerefs(builder) }.toSet(), metadata) - is SequenceLabel -> SequenceLabel(labels.map { it.replaceDerefs(builder) }, metadata) - is InvokeLabel -> { - val lut = getDerefLut(dereferences, builder) - SequenceLabel(lut.map { - StmtLabel(Assign(cast(it.value, it.value.type), - cast(it.key.map { it.replaceDerefs(lut) }, it.value.type))) - } + InvokeLabel(this.name, this.params.map { it.replaceDerefs(lut) }, metadata, - tempLookup), metadata) - } - - is StartLabel -> { - val lut = getDerefLut(dereferences, builder) - SequenceLabel(lut.map { - StmtLabel(Assign(cast(it.value, it.value.type), - cast(it.key.map { it.replaceDerefs(lut) }, it.value.type))) - } + StartLabel(name, params.map { it.replaceDerefs(lut) }, pidVar, metadata, tempLookup), metadata) - } - - is StmtLabel -> SequenceLabel(stmt.replaceDerefs(builder).map { StmtLabel(it, choiceType, metadata) }, - metadata) - - else -> error("Not implemented for ${this.javaClass.simpleName}") - } - } else this - - private fun getDerefLut(dereferences: List>, - builder: XcfaProcedureBuilder) = dereferences.associateWith { - val tmpVar = Var("__THETA_heap_tmp_$cnt", it.type) - builder.addVar(tmpVar) - tmpVar - } - - private fun Stmt.replaceDerefs(builder: XcfaProcedureBuilder): List { - val lut = getDerefLut(dereferences, builder) - val stmt: Stmt = when (this) { - is MemoryAssignStmt<*, *, *> -> if (this.deref in lut) - Assign(cast(lut[deref], expr.type), cast(expr.replaceDerefs(lut), expr.type)) - else { - MemoryAssign(deref.map { it.replaceDerefs(lut) } as Dereference<*, *, Type>, - cast(expr.replaceDerefs(lut), expr.type)) - } - - is AssignStmt<*> -> AssignStmt.of(cast(varDecl, varDecl.type), cast(expr.replaceDerefs(lut), varDecl.type)) - is AssumeStmt -> AssumeStmt.of(cond.replaceDerefs(lut) as Expr) - else -> this - } - val ret = ArrayList() - val accessType = dereferencesWithAccessTypes.filter { dereferences.contains(it.first) } - for (dereference in accessType.filter { it.second.isRead }.map { it.first }) { - ret.add(Assign(cast(lut[dereference]!!, dereference.type), - cast(dereference.map { it.replaceDerefs(lut.filter { it.key != dereference }) }, dereference.type))) + return builder + } + + private fun XcfaLabel.replaceDerefs(builder: XcfaProcedureBuilder): XcfaLabel = + if (dereferences.isNotEmpty()) { + when (this) { + is NondetLabel -> NondetLabel(labels.map { it.replaceDerefs(builder) }.toSet(), metadata) + is SequenceLabel -> SequenceLabel(labels.map { it.replaceDerefs(builder) }, metadata) + is InvokeLabel -> { + val lut = getDerefLut(dereferences, builder) + SequenceLabel( + lut.map { + StmtLabel( + Assign( + cast(it.value, it.value.type), + cast(it.key.map { it.replaceDerefs(lut) }, it.value.type), + ) + ) + } + + InvokeLabel( + this.name, + this.params.map { it.replaceDerefs(lut) }, + metadata, + tempLookup, + ), + metadata, + ) } - ret.add(stmt) - for (dereference in accessType.filter { it.second.isWritten }.map { it.first }) { - ret.add(MemoryAssign( - cast(dereference.map { it.replaceDerefs(lut.filter { it.key != dereference }) }, - dereference.type) as Dereference<*, *, Type>, - cast(lut[dereference]!!, dereference.type).ref)) + + is StartLabel -> { + val lut = getDerefLut(dereferences, builder) + SequenceLabel( + lut.map { + StmtLabel( + Assign( + cast(it.value, it.value.type), + cast(it.key.map { it.replaceDerefs(lut) }, it.value.type), + ) + ) + } + + StartLabel(name, params.map { it.replaceDerefs(lut) }, pidVar, metadata, tempLookup), + metadata, + ) } - return ret + + is StmtLabel -> + SequenceLabel( + stmt.replaceDerefs(builder).map { StmtLabel(it, choiceType, metadata) }, + metadata, + ) + + else -> error("Not implemented for ${this.javaClass.simpleName}") + } + } else this + + private fun getDerefLut(dereferences: List>, builder: XcfaProcedureBuilder) = + dereferences.associateWith { + val tmpVar = Var("__THETA_heap_tmp_$cnt", it.type) + builder.addVar(tmpVar) + tmpVar } - private fun Expr<*>.replaceDerefs(lut: Map, VarDecl<*>>): Expr<*> = - if (this in lut) { - lut[this]!!.ref - } else { - withOps(ops.map { it.replaceDerefs(lut) }) - } -} \ No newline at end of file + private fun Stmt.replaceDerefs(builder: XcfaProcedureBuilder): List { + val lut = getDerefLut(dereferences, builder) + val stmt: Stmt = + when (this) { + is MemoryAssignStmt<*, *, *> -> + if (this.deref in lut) + Assign(cast(lut[deref], expr.type), cast(expr.replaceDerefs(lut), expr.type)) + else { + MemoryAssign( + deref.map { it.replaceDerefs(lut) } as Dereference<*, *, Type>, + cast(expr.replaceDerefs(lut), expr.type), + ) + } + + is AssignStmt<*> -> + AssignStmt.of(cast(varDecl, varDecl.type), cast(expr.replaceDerefs(lut), varDecl.type)) + is AssumeStmt -> AssumeStmt.of(cond.replaceDerefs(lut) as Expr) + else -> this + } + val ret = ArrayList() + val accessType = dereferencesWithAccessTypes.filter { dereferences.contains(it.first) } + for (dereference in accessType.filter { it.second.isRead }.map { it.first }) { + ret.add( + Assign( + cast(lut[dereference]!!, dereference.type), + cast( + dereference.map { it.replaceDerefs(lut.filter { it.key != dereference }) }, + dereference.type, + ), + ) + ) + } + ret.add(stmt) + for (dereference in accessType.filter { it.second.isWritten }.map { it.first }) { + ret.add( + MemoryAssign( + cast( + dereference.map { it.replaceDerefs(lut.filter { it.key != dereference }) }, + dereference.type, + ) + as Dereference<*, *, Type>, + cast(lut[dereference]!!, dereference.type).ref, + ) + ) + } + return ret + } + + private fun Expr<*>.replaceDerefs(lut: Map, VarDecl<*>>): Expr<*> = + if (this in lut) { + lut[this]!!.ref + } else { + withOps(ops.map { it.replaceDerefs(lut) }) + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/HavocPromotionAndRange.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/HavocPromotionAndRange.kt index 7b0b3eccfa..c93b6ee723 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/HavocPromotionAndRange.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/HavocPromotionAndRange.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.VarDecl @@ -28,88 +27,94 @@ import hu.bme.mit.theta.xcfa.model.* import java.util.* /** - * This pass simplifies assignments from havoc'd intermediate variables. - * It determines intermediate variables based on their usage patterns: - * `havoc x; y := x` matches when y is not used in other contexts. - * Requires the ProcedureBuilder to be `deterministic` and `seqLbe` (@see DeterministicPass, @see LbePass) + * This pass simplifies assignments from havoc'd intermediate variables. It determines intermediate + * variables based on their usage patterns: `havoc x; y := x` matches when y is not used in other + * contexts. Requires the ProcedureBuilder to be `deterministic` and `seqLbe` (@see + * DeterministicPass, @see LbePass) */ - class HavocPromotionAndRange(val parseContext: ParseContext) : ProcedurePass { - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - checkNotNull(builder.metaData["deterministic"]) - val edges = LinkedHashSet(builder.getEdges()) - for (edge in edges) { - var candidates = (edge.label as SequenceLabel).labels - .mapIndexed { index, it -> Pair(index, it) } - .filter { - it.second is StmtLabel && - (it.second as StmtLabel).stmt is HavocStmt<*> - } - if (candidates.isNotEmpty()) { - val labelEdgeLut = LinkedHashMap, MutableList>() - edge.label.labels.forEach { - it.collectVars().forEach { v -> - labelEdgeLut.putIfAbsent(v, ArrayList()) - checkNotNull(labelEdgeLut[v]).add(it) - } - } - candidates = candidates.filter { - val v = ((it.second as StmtLabel).stmt as HavocStmt<*>).varDecl - val labels = checkNotNull(labelEdgeLut[v]) - labels.size == 2 && - labels[0] == edge.label.labels[it.first] && - labels[1] == edge.label.labels[it.first + 1] && - labels[1] is StmtLabel && (labels[1] as StmtLabel).stmt is AssignStmt<*> && - ((labels[1] as StmtLabel).stmt as AssignStmt<*>).expr == v.ref - } - val indices = candidates.map(Pair::first) - if (indices.isNotEmpty()) { - builder.removeEdge(edge) - val newLabels = ArrayList() - var offset = 0 - for ((index, label) in edge.label.labels.withIndex()) { - if (indices.size <= offset || index < indices[offset]) newLabels.add(label) - else if (index == indices[offset]) { - val varDecl = ((edge.label.labels[index + 1] as StmtLabel).stmt as AssignStmt<*>).varDecl -// val type = CComplexType.getType(((edge.label.labels[index + 1] as StmtLabel).stmt as AssignStmt<*>).expr, parseContext) - val havoc = Havoc(varDecl) - newLabels.add( - StmtLabel(havoc, metadata = edge.label.labels[index].metadata)) -// newLabels.add(StmtLabel(type.limit(varDecl.ref))) - } else if (index == indices[offset] + 1) { - offset++ - } else { - error("Should not be here") - } - } - builder.addEdge(edge.withLabel(SequenceLabel(newLabels))) - } + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + checkNotNull(builder.metaData["deterministic"]) + val edges = LinkedHashSet(builder.getEdges()) + for (edge in edges) { + var candidates = + (edge.label as SequenceLabel) + .labels + .mapIndexed { index, it -> Pair(index, it) } + .filter { it.second is StmtLabel && (it.second as StmtLabel).stmt is HavocStmt<*> } + if (candidates.isNotEmpty()) { + val labelEdgeLut = LinkedHashMap, MutableList>() + edge.label.labels.forEach { + it.collectVars().forEach { v -> + labelEdgeLut.putIfAbsent(v, ArrayList()) + checkNotNull(labelEdgeLut[v]).add(it) + } + } + candidates = + candidates.filter { + val v = ((it.second as StmtLabel).stmt as HavocStmt<*>).varDecl + val labels = checkNotNull(labelEdgeLut[v]) + labels.size == 2 && + labels[0] == edge.label.labels[it.first] && + labels[1] == edge.label.labels[it.first + 1] && + labels[1] is StmtLabel && + (labels[1] as StmtLabel).stmt is AssignStmt<*> && + ((labels[1] as StmtLabel).stmt as AssignStmt<*>).expr == v.ref + } + val indices = candidates.map(Pair::first) + if (indices.isNotEmpty()) { + builder.removeEdge(edge) + val newLabels = ArrayList() + var offset = 0 + for ((index, label) in edge.label.labels.withIndex()) { + if (indices.size <= offset || index < indices[offset]) newLabels.add(label) + else if (index == indices[offset]) { + val varDecl = + ((edge.label.labels[index + 1] as StmtLabel).stmt as AssignStmt<*>).varDecl + // val type = + // CComplexType.getType(((edge.label.labels[index + 1] as StmtLabel).stmt as + // AssignStmt<*>).expr, parseContext) + val havoc = Havoc(varDecl) + newLabels.add(StmtLabel(havoc, metadata = edge.label.labels[index].metadata)) + // newLabels.add(StmtLabel(type.limit(varDecl.ref))) + } else if (index == indices[offset] + 1) { + offset++ + } else { + error("Should not be here") } + } + builder.addEdge(edge.withLabel(SequenceLabel(newLabels))) } - val newEdges = LinkedHashSet(builder.getEdges()) - for (edge in newEdges) { - if ((edge.label as SequenceLabel).labels.any { it is StmtLabel && it.stmt is HavocStmt<*> }) { - builder.removeEdge(edge) - val list: MutableList = LinkedList(edge.label.labels) - val reversed = list.withIndex() - .filter { it.value is StmtLabel && (it.value as StmtLabel).stmt is HavocStmt<*> } - .reversed() - for ((index, value) in reversed) { - val varDecl = ((value as StmtLabel).stmt as HavocStmt<*>).varDecl - if (parseContext.metadata.getMetadataValue(varDecl.ref, "cType").isPresent) { - val type = CComplexType.getType(varDecl.ref, - parseContext) // TODO: what to do when no info is available? - if (type !is CVoid) { - list.add(index + 1, - StmtLabel(type.limit(varDecl.ref), metadata = value.metadata)) - } - } - } - builder.addEdge(edge.withLabel(SequenceLabel(list))) + } + } + val newEdges = LinkedHashSet(builder.getEdges()) + for (edge in newEdges) { + if ((edge.label as SequenceLabel).labels.any { it is StmtLabel && it.stmt is HavocStmt<*> }) { + builder.removeEdge(edge) + val list: MutableList = LinkedList(edge.label.labels) + val reversed = + list + .withIndex() + .filter { it.value is StmtLabel && (it.value as StmtLabel).stmt is HavocStmt<*> } + .reversed() + for ((index, value) in reversed) { + val varDecl = ((value as StmtLabel).stmt as HavocStmt<*>).varDecl + if (parseContext.metadata.getMetadataValue(varDecl.ref, "cType").isPresent) { + val type = + CComplexType.getType( + varDecl.ref, + parseContext, + ) // TODO: what to do when no info is available? + if (type !is CVoid) { + list.add(index + 1, StmtLabel(type.limit(varDecl.ref), metadata = value.metadata)) } + } } - - return builder + builder.addEdge(edge.withLabel(SequenceLabel(list))) + } } -} \ No newline at end of file + + return builder + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MallocFunctionPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MallocFunctionPass.kt index e655fbfd5d..0cbacc5f94 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MallocFunctionPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MallocFunctionPass.kt @@ -15,10 +15,8 @@ */ package hu.bme.mit.theta.xcfa.passes -import com.google.common.base.Preconditions.checkState import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.core.stmt.AssignStmt import hu.bme.mit.theta.core.stmt.Stmts.Assign import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add import hu.bme.mit.theta.core.type.anytype.RefExpr @@ -26,6 +24,7 @@ import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer +import hu.bme.mit.theta.xcfa.AssignStmtLabel import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* @@ -34,11 +33,15 @@ import hu.bme.mit.theta.xcfa.model.* */ class MallocFunctionPass(val parseContext: ParseContext) : ProcedurePass { - private val XcfaBuilder.malloc: VarDecl<*> by lazy { - Var("__malloc", CPointer(null, null, parseContext).smtType) + companion object { + private val mallocVars: MutableMap> = mutableMapOf() + + private fun XcfaBuilder.mallocVar(parseContext: ParseContext) = + mallocVars.getOrPut(this) { Var("__malloc", CPointer(null, null, parseContext).smtType) } } override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + val mallocVar = builder.parent.mallocVar(parseContext) checkNotNull(builder.metaData["deterministic"]) for (edge in ArrayList(builder.getEdges())) { val edges = edge.splitIf(this::predicate) @@ -47,18 +50,17 @@ class MallocFunctionPass(val parseContext: ParseContext) : ProcedurePass { (edges.size == 1 && predicate((edges[0].label as SequenceLabel).labels[0])) ) { builder.removeEdge(edge) - edges.forEach { - if (predicate((it.label as SequenceLabel).labels[0])) { - val invokeLabel = it.label.labels[0] as InvokeLabel + edges.forEach { e -> + if (predicate((e.label as SequenceLabel).labels[0])) { + val invokeLabel = e.label.labels[0] as InvokeLabel val ret = invokeLabel.params[0] as RefExpr<*> - val mallocVar = builder.parent.malloc if (builder.parent.getVars().none { it.wrappedVar == mallocVar }) { // initial creation builder.parent.addVar( XcfaGlobalVar(mallocVar, CComplexType.getType(ret, parseContext).nullValue) ) val initProc = builder.parent.getInitProcedures().map { it.first } - checkState(initProc.size == 1, "Multiple start procedure are not handled well") - initProc.forEach { + check(initProc.size == 1) { "Multiple start procedure are not handled well" } + initProc.forEach { proc -> val initAssign = StmtLabel( Assign( @@ -67,7 +69,7 @@ class MallocFunctionPass(val parseContext: ParseContext) : ProcedurePass { ) ) val newEdges = - it.initLoc.outgoingEdges.map { + proc.initLoc.outgoingEdges.map { it.withLabel( SequenceLabel( listOf(initAssign) + it.label.getFlatLabels(), @@ -75,35 +77,23 @@ class MallocFunctionPass(val parseContext: ParseContext) : ProcedurePass { ) ) } - it.initLoc.outgoingEdges.forEach(it::removeEdge) - newEdges.forEach(it::addEdge) + proc.initLoc.outgoingEdges.forEach(proc::removeEdge) + newEdges.forEach(proc::addEdge) } } val assign1 = - AssignStmt.of( - cast(mallocVar, ret.type), - cast( - Add(mallocVar.ref, CComplexType.getType(ret, parseContext).getValue("3")), - ret.type, - ), + AssignStmtLabel( + mallocVar, + Add(mallocVar.ref, CComplexType.getType(ret, parseContext).getValue("3")), + ret.type, + invokeLabel.metadata, ) - val assign2 = - AssignStmt.of(cast(ret.decl as VarDecl<*>, ret.type), cast(mallocVar.ref, ret.type)) + val assign2 = AssignStmtLabel(ret, cast(mallocVar.ref, ret.type)) builder.addEdge( - XcfaEdge( - it.source, - it.target, - SequenceLabel( - listOf( - StmtLabel(assign1, metadata = invokeLabel.metadata), - StmtLabel(assign2, metadata = invokeLabel.metadata), - ) - ), - it.metadata, - ) + XcfaEdge(e.source, e.target, SequenceLabel(listOf(assign1, assign2)), e.metadata) ) } else { - builder.addEdge(it) + builder.addEdge(e) } } } diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MutexToVarPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MutexToVarPass.kt index f61bc4e10e..fda7623a5c 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MutexToVarPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MutexToVarPass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.Decls @@ -26,90 +25,100 @@ import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.model.* /** - * Replaces mutexes (except the atomic block mutexes) with boolean variables. - * mutex_lock(mutex_var) -> assume(!mutex_var); mutex_var := true; (atomically) - * mutex_unlock(mutex_var) -> mutex_var := false; + * Replaces mutexes (except the atomic block mutexes) with boolean variables. mutex_lock(mutex_var) + * -> assume(!mutex_var); mutex_var := true; (atomically) mutex_unlock(mutex_var) -> mutex_var := + * false; */ class MutexToVarPass : ProcedurePass { - private val mutexVars = mutableMapOf>() + private val mutexVars = mutableMapOf>() - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - builder.parent.getVars().forEach { (v) -> - if (v.type == BoolType.getInstance()) { - mutexVars[v.name] = v as VarDecl - } - } + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + builder.parent.getVars().forEach { (v) -> + if (v.type == BoolType.getInstance()) { + mutexVars[v.name] = v as VarDecl + } + } - builder.getEdges().toSet().forEach { edge -> - builder.removeEdge(edge) - builder.addEdge(edge.withLabel(edge.label.replaceMutex())) - } + builder.getEdges().toSet().forEach { edge -> + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(edge.label.replaceMutex())) + } - mutexVars.forEach { (_, v) -> builder.parent.addVar(XcfaGlobalVar(v, False())) } - builder.parent.getInitProcedures().forEach { (proc, _) -> - mutexVars.forEach { (_, v) -> - val initEdge = proc.initLoc.outgoingEdges.first() - val initLabels = initEdge.getFlatLabels() - if (initLabels.none { it is StmtLabel && it.stmt is AssignStmt<*> && it.stmt.varDecl == v }) { - val assign = StmtLabel(AssignStmt.of(v, False())) - val label = SequenceLabel(initLabels + assign, metadata = initEdge.label.metadata) - proc.removeEdge(initEdge) - proc.addEdge(initEdge.withLabel(label)) - } - } + mutexVars.forEach { (_, v) -> builder.parent.addVar(XcfaGlobalVar(v, False())) } + builder.parent.getInitProcedures().forEach { (proc, _) -> + mutexVars.forEach { (_, v) -> + val initEdge = proc.initLoc.outgoingEdges.first() + val initLabels = initEdge.getFlatLabels() + if ( + initLabels.none { it is StmtLabel && it.stmt is AssignStmt<*> && it.stmt.varDecl == v } + ) { + val assign = StmtLabel(AssignStmt.of(v, False())) + val label = SequenceLabel(initLabels + assign, metadata = initEdge.label.metadata) + proc.removeEdge(initEdge) + proc.addEdge(initEdge.withLabel(label)) } - return builder + } } + return builder + } + + private fun XcfaLabel.replaceMutex(): XcfaLabel { + return when (this) { + is SequenceLabel -> SequenceLabel(labels.map { it.replaceMutex() }, metadata) + is FenceLabel -> { + val actions = mutableListOf() - private fun XcfaLabel.replaceMutex(): XcfaLabel { - return when (this) { - is SequenceLabel -> SequenceLabel(labels.map { it.replaceMutex() }, metadata) - is FenceLabel -> { - val actions = mutableListOf() - - labels.forEach { l -> - if (l == "ATOMIC_BEGIN") { - actions.add(FenceLabel(setOf("ATOMIC_BEGIN"))) - return@forEach - } - if (l == "ATOMIC_END") { - actions.add(FenceLabel(setOf("ATOMIC_END"))) - return@forEach - } - - if (Regex("start_cond_wait\\((.*)\\)").matches(l)) { - val args = l.substring("start_cond_wait".length + 1, l.length - 1).split(",") - actions.add(StmtLabel(AssignStmt.of(args[0].signalFlag, False()))) - } - if (Regex("cond_wait\\((.*)\\)").matches(l)) { - val args = l.substring("cond_wait".length + 1, l.length - 1).split(",") - actions.add(StmtLabel(AssumeStmt.of(args[0].signalFlag.ref))) - } - if (Regex("cond_signal\\((.*)\\)").matches(l)) { - val arg = l.substring("cond_signal".length + 1, l.length - 1) - actions.add(StmtLabel(AssignStmt.of(arg.signalFlag, True()))) - } - - l.acquiredMutex?.let { - actions.add(StmtLabel(AssumeStmt.of(Not(it.mutexFlag.ref)))) - actions.add(StmtLabel(AssignStmt.of(it.mutexFlag, True()))) - } - l.releasedMutex?.let { - actions.add(StmtLabel(AssignStmt.of(it.mutexFlag, False()))) - } - } - - SequenceLabel(actions) // Labels are atomic in XCFA semantics: no need to wrap them in an atomic block - } - - else -> this + labels.forEach { l -> + if (l == "pthread_exit") { + actions.add(FenceLabel(setOf(l))) + return@forEach + } + + if (l == "ATOMIC_BEGIN") { + actions.add(FenceLabel(setOf("ATOMIC_BEGIN"))) + return@forEach + } + if (l == "ATOMIC_END") { + actions.add(FenceLabel(setOf("ATOMIC_END"))) + return@forEach + } + + if (Regex("start_cond_wait\\((.*)\\)").matches(l)) { + val args = l.substring("start_cond_wait".length + 1, l.length - 1).split(",") + actions.add(StmtLabel(AssignStmt.of(args[0].signalFlag, False()))) + } + if (Regex("cond_wait\\((.*)\\)").matches(l)) { + val args = l.substring("cond_wait".length + 1, l.length - 1).split(",") + actions.add(StmtLabel(AssumeStmt.of(args[0].signalFlag.ref))) + } + if (Regex("cond_signal\\((.*)\\)").matches(l)) { + val arg = l.substring("cond_signal".length + 1, l.length - 1) + actions.add(StmtLabel(AssignStmt.of(arg.signalFlag, True()))) + } + + l.acquiredMutex?.let { + actions.add(StmtLabel(AssumeStmt.of(Not(it.mutexFlag.ref)))) + actions.add(StmtLabel(AssignStmt.of(it.mutexFlag, True()))) + } + l.releasedMutex?.let { actions.add(StmtLabel(AssignStmt.of(it.mutexFlag, False()))) } } + + SequenceLabel( + actions + ) // Labels are atomic in XCFA semantics: no need to wrap them in an atomic block + } + + else -> this } + } + + private val String.mutexFlag + get() = flag("_mutex_flag") - private val String.mutexFlag get() = flag("_mutex_flag") - private val String.signalFlag get() = flag("_signal_flag") + private val String.signalFlag + get() = flag("_signal_flag") - private fun String.flag(prefix: String) = - mutexVars.getOrPut(this) { Decls.Var("${prefix}_${ifEmpty { "atomic" }}", Bool()) } -} \ No newline at end of file + private fun String.flag(prefix: String) = + mutexVars.getOrPut(this) { Decls.Var("${prefix}_${ifEmpty { "atomic" }}", Bool()) } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NormalizePass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NormalizePass.kt index 418090364d..7222ac96e2 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NormalizePass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NormalizePass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.stmt.AssumeStmt @@ -21,53 +20,53 @@ import hu.bme.mit.theta.core.type.booltype.BoolExprs.True import hu.bme.mit.theta.xcfa.model.* /** - * This pass converts all edges to a normalized form, i.e., the outermost label is a NonDetLabel containing - * SequenceLabels, which do not contain any more Sequence or NonDet labels. - * Sets the `normal` flag on the ProcedureBuilder + * This pass converts all edges to a normalized form, i.e., the outermost label is a NonDetLabel + * containing SequenceLabels, which do not contain any more Sequence or NonDet labels. Sets the + * `normal` flag on the ProcedureBuilder */ - class NormalizePass : ProcedurePass { - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - val edges = LinkedHashSet(builder.getEdges()) - for (edge in edges) { - builder.removeEdge(edge) - builder.addEdge(edge.withLabel(normalize(edge.label))) - } - builder.metaData["normal"] = Unit - return builder + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + val edges = LinkedHashSet(builder.getEdges()) + for (edge in edges) { + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(normalize(edge.label))) } + builder.metaData["normal"] = Unit + return builder + } - private fun normalize(label: XcfaLabel): XcfaLabel { - val collector: MutableList> = ArrayList() - collector.add(ArrayList()) - normalize(label, collector) - return NondetLabel(collector.map { SequenceLabel(it) }.toSet()) - } + private fun normalize(label: XcfaLabel): XcfaLabel { + val collector: MutableList> = ArrayList() + collector.add(ArrayList()) + normalize(label, collector) + return NondetLabel(collector.map { SequenceLabel(it) }.toSet()) + } - private fun normalize(label: XcfaLabel, collector: MutableList>) { - when (label) { - is SequenceLabel -> label.labels.forEach { normalize(it, collector) } - is NondetLabel -> { - val labelList = label.labels.toList() - ArrayList(collector).forEach { list -> - for ((i, xcfaLabel) in labelList.withIndex()) { - if (i == labelList.size - 1) { - list.add(xcfaLabel) - } else { - val newList = ArrayList(list) - newList.add(xcfaLabel) - collector.add(newList) - } - } - } + private fun normalize(label: XcfaLabel, collector: MutableList>) { + when (label) { + is SequenceLabel -> label.labels.forEach { normalize(it, collector) } + is NondetLabel -> { + val labelList = label.labels.toList() + ArrayList(collector).forEach { list -> + for ((i, xcfaLabel) in labelList.withIndex()) { + if (i == labelList.size - 1) { + list.add(xcfaLabel) + } else { + val newList = ArrayList(list) + newList.add(xcfaLabel) + collector.add(newList) } + } + } + } - is NopLabel -> {} - is StmtLabel -> if (!(label.stmt is AssumeStmt && label.stmt.cond.equals( - True()))) collector.forEach { it.add(label) } + is NopLabel -> {} + is StmtLabel -> + if (!(label.stmt is AssumeStmt && label.stmt.cond.equals(True()))) + collector.forEach { it.add(label) } - else -> collector.forEach { it.add(label) } - } + else -> collector.forEach { it.add(label) } } -} \ No newline at end of file + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ProcedurePassManager.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ProcedurePassManager.kt index 02cb00683e..59949a3a7d 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ProcedurePassManager.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ProcedurePassManager.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.common.logging.Logger @@ -21,83 +20,81 @@ import hu.bme.mit.theta.frontend.ParseContext open class ProcedurePassManager(vararg passes: List) { - val passes: List> = passes.toList() + val passes: List> = passes.toList() } -class CPasses(checkOverflow: Boolean, parseContext: ParseContext, uniqueWarningLogger: Logger) : ProcedurePassManager( +class CPasses(checkOverflow: Boolean, parseContext: ParseContext, uniqueWarningLogger: Logger) : + ProcedurePassManager( listOf( - // formatting - NormalizePass(), - DeterministicPass(), - // removing redundant elements - EmptyEdgeRemovalPass(), - UnusedLocRemovalPass(), - // handling intrinsics - ErrorLocationPass(checkOverflow), - FinalLocationPass(checkOverflow), - SvCompIntrinsicsPass(), - FpFunctionsToExprsPass(parseContext), - CLibraryFunctionsPass(), + // formatting + NormalizePass(), + DeterministicPass(), + // removing redundant elements + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), + // handling intrinsics + ErrorLocationPass(checkOverflow), + FinalLocationPass(checkOverflow), + SvCompIntrinsicsPass(), + FpFunctionsToExprsPass(parseContext), + CLibraryFunctionsPass(), ), + listOf(ReferenceElimination(parseContext), MallocFunctionPass(parseContext)), listOf( - // trying to inline procedures - InlineProceduresPass(parseContext), - RemoveDeadEnds(), - EliminateSelfLoops(), + // optimizing + SimplifyExprsPass(parseContext), + LoopUnrollPass(), + SimplifyExprsPass(parseContext), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), ), listOf( - ReferenceElimination(parseContext), - MallocFunctionPass(parseContext), + // trying to inline procedures + InlineProceduresPass(parseContext), + RemoveDeadEnds(), + EliminateSelfLoops(), ), + listOf(StaticCoiPass()), listOf( - // optimizing - SimplifyExprsPass(parseContext), - LoopUnrollPass(), - SimplifyExprsPass(parseContext), - EmptyEdgeRemovalPass(), - UnusedLocRemovalPass(), + // handling remaining function calls + NoSideEffectPass(parseContext), + NondetFunctionPass(), + LbePass(parseContext), + NormalizePass(), // needed after lbe, TODO + DeterministicPass(), // needed after lbe, TODO + HavocPromotionAndRange(parseContext), + // Final cleanup + UnusedVarPass(uniqueWarningLogger), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), ), + listOf(FetchExecuteWriteback(parseContext)), + ) + +class ChcPasses(parseContext: ParseContext, uniqueWarningLogger: Logger) : + ProcedurePassManager( listOf( - StaticCoiPass(), + // formatting + NormalizePass(), + DeterministicPass(), + // removing redundant elements + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), + // optimizing + SimplifyExprsPass(parseContext), ), listOf( - // handling remaining function calls - NoSideEffectPass(parseContext), - NondetFunctionPass(), - LbePass(parseContext), - NormalizePass(), // needed after lbe, TODO - DeterministicPass(), // needed after lbe, TODO - HavocPromotionAndRange(parseContext), - // Final cleanup -// UnusedVarPass(uniqueWarningLogger), - EmptyEdgeRemovalPass(), - UnusedLocRemovalPass(), + // trying to inline procedures + InlineProceduresPass(parseContext), + RemoveDeadEnds(), + EliminateSelfLoops(), + // handling remaining function calls + LbePass(parseContext), + NormalizePass(), // needed after lbe, TODO + DeterministicPass(), // needed after lbe, TODO + // Final cleanup + UnusedVarPass(uniqueWarningLogger), ), - listOf( - FetchExecuteWriteback(parseContext) - ) -) - -class ChcPasses(parseContext: ParseContext, uniqueWarningLogger: Logger) : ProcedurePassManager(listOf( - // formatting - NormalizePass(), - DeterministicPass(), - // removing redundant elements - EmptyEdgeRemovalPass(), - UnusedLocRemovalPass(), - // optimizing - SimplifyExprsPass(parseContext), -), listOf( - // trying to inline procedures - InlineProceduresPass(parseContext), - RemoveDeadEnds(), - EliminateSelfLoops(), - // handling remaining function calls - LbePass(parseContext), - NormalizePass(), // needed after lbe, TODO - DeterministicPass(), // needed after lbe, TODO - // Final cleanup - UnusedVarPass(uniqueWarningLogger), -)) + ) -class LitmusPasses : ProcedurePassManager() \ No newline at end of file +class LitmusPasses : ProcedurePassManager() diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ReferenceElimination.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ReferenceElimination.kt index 9b8cb78acc..9a10822fc4 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ReferenceElimination.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ReferenceElimination.kt @@ -19,7 +19,6 @@ import com.google.common.base.Preconditions.checkState import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.* -import hu.bme.mit.theta.core.stmt.Stmts.Assign import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.Type import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add @@ -31,6 +30,7 @@ import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer +import hu.bme.mit.theta.xcfa.AssignStmtLabel import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.references @@ -42,18 +42,22 @@ class ReferenceElimination(val parseContext: ParseContext) : ProcedurePass { private var cnt = 2 // counts upwards, uses 3k+2 get() = field.also { field += 3 } - } - private val XcfaBuilder.pointer: VarDecl<*> by lazy { - Var("__sp", CPointer(null, null, parseContext).smtType) + private val ptrVars: MutableMap> = mutableMapOf() + + private fun XcfaBuilder.ptrVar(parseContext: ParseContext) = + ptrVars.getOrPut(this) { Var("__sp", CPointer(null, null, parseContext).smtType) } } override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + val ptrVar = builder.parent.ptrVar(parseContext) val globalReferredVars = builder.parent.metaData.computeIfAbsent("references") { builder.parent .getProcedures() - .flatMap { it.getEdges().flatMap { it.label.getFlatLabels().flatMap { it.references } } } + .flatMap { p -> + p.getEdges().flatMap { it -> it.label.getFlatLabels().flatMap { it.references } } + } .map { (it.expr as RefExpr<*>).decl as VarDecl<*> } .toSet() .filter { builder.parent.getVars().any { global -> global.wrappedVar == it } } @@ -63,8 +67,7 @@ class ReferenceElimination(val parseContext: ParseContext) : ProcedurePass { val lit = CComplexType.getType(varDecl.ref, parseContext).getValue("$cnt") builder.parent.addVar(XcfaGlobalVar(varDecl, lit)) parseContext.metadata.create(varDecl.ref, "cType", ptrType) - val assign = - StmtLabel(AssignStmt.of(cast(varDecl, varDecl.type), cast(lit, varDecl.type))) + val assign = AssignStmtLabel(varDecl, lit) Pair(varDecl, SequenceLabel(listOf(assign))) } } @@ -74,85 +77,74 @@ class ReferenceElimination(val parseContext: ParseContext) : ProcedurePass { val referredVars = builder .getEdges() - .flatMap { it.label.getFlatLabels().flatMap { it.references } } + .flatMap { e -> e.label.getFlatLabels().flatMap { it.references } } .map { (it.expr as RefExpr<*>).decl as VarDecl<*> } .toSet() .filter { !globalReferredVars.containsKey(it) } - .associateWith { - val ptrType = CPointer(null, CComplexType.getType(it.ref, parseContext), parseContext) + .associateWith { v -> + val ptrType = CPointer(null, CComplexType.getType(v.ref, parseContext), parseContext) - val ptrVar = builder.parent.pointer if (builder.parent.getVars().none { it.wrappedVar == ptrVar }) { // initial creation val initVal = ptrType.getValue("$cnt") builder.parent.addVar(XcfaGlobalVar(ptrVar, initVal)) val initProc = builder.parent.getInitProcedures().map { it.first } checkState(initProc.size == 1, "Multiple start procedure are not handled well") - initProc.forEach { - val initAssign = - StmtLabel(Assign(cast(ptrVar, ptrVar.type), cast(initVal, ptrVar.type))) + initProc.forEach { proc -> + val initAssign = AssignStmtLabel(ptrVar, initVal) val newEdges = - it.initLoc.outgoingEdges.map { + proc.initLoc.outgoingEdges.map { it.withLabel( SequenceLabel(listOf(initAssign) + it.label.getFlatLabels(), it.label.metadata) ) } - it.initLoc.outgoingEdges.forEach(it::removeEdge) - newEdges.forEach(it::addEdge) + proc.initLoc.outgoingEdges.forEach(proc::removeEdge) + newEdges.forEach(proc::addEdge) } } val assign1 = - StmtLabel( - AssignStmt.of( - cast(ptrVar, ptrType.smtType), - cast(Add(ptrVar.ref, ptrType.getValue("3")), ptrType.smtType), - ) - ) - val varDecl = Var(it.name + "*", ptrType.smtType) + AssignStmtLabel(ptrVar, Add(ptrVar.ref, ptrType.getValue("3")), ptrType.smtType) + val varDecl = Var(v.name + "*", ptrType.smtType) builder.addVar(varDecl) parseContext.metadata.create(varDecl.ref, "cType", ptrType) - val assign2 = - StmtLabel(AssignStmt.of(cast(varDecl, varDecl.type), cast(ptrVar.ref, varDecl.type))) + val assign2 = AssignStmtLabel(varDecl, ptrVar.ref) Pair(varDecl, SequenceLabel(listOf(assign1, assign2))) } - if ( - globalReferredVars.isNotEmpty() && - builder.parent.getInitProcedures().any { it.first == builder } - ) { // we only need this for main - val initLabels = globalReferredVars.values.flatMap { it.second.labels } - val initEdges = builder.initLoc.outgoingEdges - val newInitEdges = - initEdges.map { - it.withLabel(SequenceLabel(initLabels + it.label.getFlatLabels(), it.label.metadata)) - } - initEdges.forEach(builder::removeEdge) - newInitEdges.forEach(builder::addEdge) - } - if (referredVars.isNotEmpty()) { - val initLabels = referredVars.values.flatMap { it.second.labels } - val initEdges = builder.initLoc.outgoingEdges - val newInitEdges = - initEdges.map { - it.withLabel(SequenceLabel(initLabels + it.label.getFlatLabels(), it.label.metadata)) - } - initEdges.forEach(builder::removeEdge) - newInitEdges.forEach(builder::addEdge) + if (builder.parent.getInitProcedures().any { it.first == builder }) { + addRefInitializations(builder, globalReferredVars) // we only need this for main + } + addRefInitializations(builder, referredVars) + val allReferredVars = referredVars + globalReferredVars + if (allReferredVars.isNotEmpty()) { val edges = LinkedHashSet(builder.getEdges()) - val allReferredVars = referredVars + globalReferredVars for (edge in edges) { builder.removeEdge(edge) builder.addEdge( edge.withLabel(edge.label.changeReferredVars(allReferredVars, parseContext)) ) } - return DeterministicPass().run(NormalizePass().run(builder)) } return builder } + private fun addRefInitializations( + builder: XcfaProcedureBuilder, + referredVars: Map, Pair, SequenceLabel>>, + ) { + if (referredVars.isEmpty()) return + val initLabels = referredVars.values.flatMap { it.second.labels } + val initEdges = builder.initLoc.outgoingEdges + val newInitEdges = + initEdges.map { + it.withLabel(SequenceLabel(initLabels + it.label.getFlatLabels(), it.label.metadata)) + } + initEdges.forEach(builder::removeEdge) + newInitEdges.forEach(builder::addEdge) + } + @JvmOverloads fun XcfaLabel.changeReferredVars( varLut: Map, Pair, SequenceLabel>>, @@ -275,7 +267,7 @@ class ReferenceElimination(val parseContext: ParseContext) : ProcedurePass { ret } - fun VarDecl.changeReferredVars( + private fun VarDecl.changeReferredVars( varLut: Map, Pair, XcfaLabel>> ): Expr = varLut[this]?.first?.let { diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SvCompIntrinsicsPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SvCompIntrinsicsPass.kt index 46933ea4e9..f97e1adf74 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SvCompIntrinsicsPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SvCompIntrinsicsPass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.xcfa.model.* @@ -21,62 +20,63 @@ import kotlin.jvm.optionals.getOrNull /** * Transforms the following SV-COMP intrinsics into model elements: - * - __VERIFIER_atomic_begin() - * - __VERIFIER_atomic_end() - * - __VERIFIER_atomic_* - * Requires the ProcedureBuilder be `deterministic`. + * - __VERIFIER_atomic_begin() + * - __VERIFIER_atomic_end() + * - __VERIFIER_atomic_* Requires the ProcedureBuilder be `deterministic`. */ @OptIn(ExperimentalStdlibApi::class) class SvCompIntrinsicsPass : ProcedurePass { - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - checkNotNull(builder.metaData["deterministic"]) - if (builder.name.startsWith("__VERIFIER_atomic")) { - for (outgoingEdge in ArrayList(builder.initLoc.outgoingEdges)) { - builder.removeEdge(outgoingEdge) - val labels: MutableList = ArrayList() - labels.add(FenceLabel(setOf("ATOMIC_BEGIN"), metadata = outgoingEdge.metadata)) - labels.addAll((outgoingEdge.label as SequenceLabel).labels) - builder.addEdge(outgoingEdge.withLabel(SequenceLabel(labels))) - } - for (incomingEdge in ArrayList( - builder.finalLoc.getOrNull()?.incomingEdges ?: listOf())) { - builder.removeEdge(incomingEdge) - val labels = ArrayList((incomingEdge.label as SequenceLabel).labels) - labels.add(FenceLabel(setOf("ATOMIC_END"), metadata = incomingEdge.metadata)) - builder.addEdge(incomingEdge.withLabel(SequenceLabel(labels))) - } - } - for (edge in ArrayList(builder.getEdges())) { - val edges = edge.splitIf(this::predicate) - if (edges.size > 1 || (edges.size == 1 && predicate( - (edges[0].label as SequenceLabel).labels[0]))) { - builder.removeEdge(edge) - val labels: MutableList = ArrayList() - edges.forEach { - if (predicate((it.label as SequenceLabel).labels[0])) { - val invokeLabel = it.label.labels[0] as InvokeLabel - val fence = when (invokeLabel.name) { - "__VERIFIER_atomic_begin" -> FenceLabel(setOf("ATOMIC_BEGIN"), - metadata = invokeLabel.metadata) + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + checkNotNull(builder.metaData["deterministic"]) + if (builder.name.startsWith("__VERIFIER_atomic")) { + for (outgoingEdge in ArrayList(builder.initLoc.outgoingEdges)) { + builder.removeEdge(outgoingEdge) + val labels: MutableList = ArrayList() + labels.add(FenceLabel(setOf("ATOMIC_BEGIN"), metadata = outgoingEdge.metadata)) + labels.addAll((outgoingEdge.label as SequenceLabel).labels) + builder.addEdge(outgoingEdge.withLabel(SequenceLabel(labels))) + } + for (incomingEdge in ArrayList(builder.finalLoc.getOrNull()?.incomingEdges ?: listOf())) { + builder.removeEdge(incomingEdge) + val labels = ArrayList((incomingEdge.label as SequenceLabel).labels) + labels.add(FenceLabel(setOf("ATOMIC_END"), metadata = incomingEdge.metadata)) + builder.addEdge(incomingEdge.withLabel(SequenceLabel(labels))) + } + } + for (edge in ArrayList(builder.getEdges())) { + val edges = edge.splitIf(this::predicate) + if ( + edges.size > 1 || + (edges.size == 1 && predicate((edges[0].label as SequenceLabel).labels[0])) + ) { + builder.removeEdge(edge) + val labels: MutableList = ArrayList() + edges.forEach { + if (predicate((it.label as SequenceLabel).labels[0])) { + val invokeLabel = it.label.labels[0] as InvokeLabel + val fence = + when (invokeLabel.name) { + "__VERIFIER_atomic_begin" -> + FenceLabel(setOf("ATOMIC_BEGIN"), metadata = invokeLabel.metadata) - "__VERIFIER_atomic_end" -> FenceLabel(setOf("ATOMIC_END"), - metadata = invokeLabel.metadata) + "__VERIFIER_atomic_end" -> + FenceLabel(setOf("ATOMIC_END"), metadata = invokeLabel.metadata) - else -> invokeLabel - } - labels.add(fence) - } else { - labels.addAll(it.label.labels) - } - } - builder.addEdge(edge.withLabel(SequenceLabel(labels))) - } + else -> invokeLabel + } + labels.add(fence) + } else { + labels.addAll(it.label.labels) + } } - return builder + builder.addEdge(edge.withLabel(SequenceLabel(labels))) + } } + return builder + } - private fun predicate(it: XcfaLabel): Boolean { - return it is InvokeLabel && it.name.startsWith("__VERIFIER_atomic") - } -} \ No newline at end of file + private fun predicate(it: XcfaLabel): Boolean { + return it is InvokeLabel && it.name.startsWith("__VERIFIER_atomic") + } +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedVarPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedVarPass.kt index 1f88251fab..13e3e6d808 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedVarPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedVarPass.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import com.google.common.collect.Sets @@ -26,67 +25,75 @@ import hu.bme.mit.theta.xcfa.isRead import hu.bme.mit.theta.xcfa.model.* /** - * Remove unused variables from the program. - * Requires the ProcedureBuilder to be `deterministic` (@see DeterministicPass) + * Remove unused variables from the program. Requires the ProcedureBuilder to be `deterministic` + * (@see DeterministicPass) */ class UnusedVarPass(private val uniqueWarningLogger: Logger) : ProcedurePass { - override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { - checkNotNull(builder.metaData["deterministic"]) - - val usedVars = LinkedHashSet>() - - var edges = LinkedHashSet(builder.parent.getProcedures().flatMap { it.getEdges() }) - lateinit var lastEdges: Set - do { - lastEdges = edges - - usedVars.clear() - edges.forEach { edge -> - usedVars.addAll(edge.label.collectVarsWithAccessType().filter { it.value.isRead }.map { it.key }) - } - - builder.parent.getProcedures().forEach { b -> - b.getEdges().toList().forEach { edge -> - val newLabel = edge.label.removeUnusedWrites(usedVars) - if (newLabel != edge.label) { - b.removeEdge(edge) - b.addEdge(edge.withLabel(newLabel)) - } - } - } - - edges = LinkedHashSet(builder.parent.getProcedures().flatMap { it.getEdges() }) - } while (lastEdges != edges) - - val allVars = Sets.union(builder.getVars(), builder.parent.getVars().map { it.wrappedVar }.toSet()) - val varsAndParams = Sets.union(allVars, builder.getParams().map { it.first }.toSet()) - if (!varsAndParams.containsAll(usedVars)) { - uniqueWarningLogger.write(Logger.Level.INFO, - "WARNING: There are some used variables not present as declarations: " + - "${usedVars.filter { it !in varsAndParams }}\n") + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + checkNotNull(builder.metaData["deterministic"]) + + val usedVars = LinkedHashSet>() + + var edges = LinkedHashSet(builder.parent.getProcedures().flatMap { it.getEdges() }) + lateinit var lastEdges: Set + do { + lastEdges = edges + + usedVars.clear() + edges.forEach { edge -> + usedVars.addAll( + edge.label.collectVarsWithAccessType().filter { it.value.isRead }.map { it.key } + ) + } + + builder.parent.getProcedures().forEach { b -> + b.getEdges().toList().forEach { edge -> + val newLabel = edge.label.removeUnusedWrites(usedVars) + if (newLabel != edge.label) { + b.removeEdge(edge) + b.addEdge(edge.withLabel(newLabel)) + } } - - builder.getVars().filter { it !in usedVars }.forEach { builder.removeVar(it) } - - return builder + } + + edges = LinkedHashSet(builder.parent.getProcedures().flatMap { it.getEdges() }) + } while (lastEdges != edges) + + val allVars = + Sets.union(builder.getVars(), builder.parent.getVars().map { it.wrappedVar }.toSet()) + val varsAndParams = Sets.union(allVars, builder.getParams().map { it.first }.toSet()) + if (!varsAndParams.containsAll(usedVars)) { + uniqueWarningLogger.writeln( + Logger.Level.INFO, + "WARNING: There are some used variables not present as declarations: " + + usedVars.filter { it !in varsAndParams }, + ) } - private fun XcfaLabel.removeUnusedWrites(usedVars: Set>): XcfaLabel { - return when (this) { - is SequenceLabel -> - SequenceLabel(labels.map { it.removeUnusedWrites(usedVars) }.filter { it !is NopLabel }) + builder.getVars().filter { it !in usedVars }.forEach { builder.removeVar(it) } - is NondetLabel -> - NondetLabel(labels.map { it.removeUnusedWrites(usedVars) }.filter { it !is NopLabel }.toSet()) + return builder + } - is StmtLabel -> when (stmt) { - is AssignStmt<*> -> if (stmt.varDecl in usedVars) this else NopLabel - is HavocStmt<*> -> if (stmt.varDecl in usedVars) this else NopLabel - else -> this - } + private fun XcfaLabel.removeUnusedWrites(usedVars: Set>): XcfaLabel { + return when (this) { + is SequenceLabel -> + SequenceLabel(labels.map { it.removeUnusedWrites(usedVars) }.filter { it !is NopLabel }) - else -> this + is NondetLabel -> + NondetLabel( + labels.map { it.removeUnusedWrites(usedVars) }.filter { it !is NopLabel }.toSet() + ) + + is StmtLabel -> + when (stmt) { + is AssignStmt<*> -> if (stmt.varDecl in usedVars) this else NopLabel + is HavocStmt<*> -> if (stmt.varDecl in usedVars) this else NopLabel + else -> this } + + else -> this } -} \ No newline at end of file + } +} diff --git a/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/PassTests.kt b/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/PassTests.kt index 570ab2b126..d03c92478c 100644 --- a/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/PassTests.kt +++ b/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/PassTests.kt @@ -30,7 +30,6 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cint import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CFloat import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -40,583 +39,417 @@ import org.junit.jupiter.params.provider.MethodSource class PassTests { + class PassTestData( + global: VarContext.() -> Unit, + input: XcfaProcedureBuilderContext.() -> Unit, + output: (XcfaProcedureBuilderContext.() -> Unit)?, + val passes: List, + ) : Arguments { - class PassTestData( - global: VarContext.() -> Unit, - input: XcfaProcedureBuilderContext.() -> Unit, - output: (XcfaProcedureBuilderContext.() -> Unit)?, - val passes: List) : Arguments { + private val builder = XcfaBuilder("").also { it.global(global) } + private val inputBuilder = builder.procedure("", input).builder + private val outputBuilder = output?.let { builder.procedure("", it).builder } - private val builder = XcfaBuilder("").also { it.global(global) } - private val inputBuilder = builder.procedure("", input).builder - private val outputBuilder = output?.let { builder.procedure("", it).builder } + override fun get(): Array = Arguments.of(inputBuilder, outputBuilder, passes).get() + } + companion object { - override fun get(): Array = Arguments.of(inputBuilder, outputBuilder, passes).get() - } - - companion object { - - private val dummyXcfa = xcfa("") {} - private val parseContext = ParseContext() - private val fpParseContext = ParseContext().also { it.arithmetic = ArchitectureConfig.ArithmeticType.bitvector } + private val dummyXcfa = xcfa("") {} + private val parseContext = ParseContext() + private val fpParseContext = + ParseContext().also { it.arithmetic = ArchitectureConfig.ArithmeticType.bitvector } - @JvmStatic - val data: List = listOf( - PassTestData( - global = { "x" type Int() init "0"; "y" type Int() init "0" }, - passes = listOf( - NormalizePass(), - DeterministicPass() - ), - input = { - (init to final) { - nondet { - havoc("x") - havoc("y") - } - } - }, - output = { - (init to final) { - havoc("x") - } - (init to final) { - havoc("y") - } - }, - ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - EliminateSelfLoops(), - LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_SEQ }, - ), - input = { - (init to "L1") { - assume("1 == 1") - } - ("L1" to final) { - assume("false") - } - }, - output = { - (init to final) { - assume("1 == 1") - assume("false") - } - }, - ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - EliminateSelfLoops(), - LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_FULL }, - ), - input = { - (init to "L1") { - assume("1 == 1") - } - ("L1" to "L2") { - assume("1 == 1") - } - ("L2" to final) { - assume("false") - } - ("L2" to final) { - assume("1 == 2") - } - }, - output = { - (init to final) { - assume("1 == 1") - assume("1 == 1") - nondet { - assume("false") - assume("1 == 2") - } - } - }, - ), - PassTestData( - global = { }, - passes = listOf( - EmptyEdgeRemovalPass(), - UnusedLocRemovalPass() - ), - input = { - (init to "L1") { - nop() - } - ("L1" to "L2") { - nop() - } - ("L2" to final) { - nop() - } - }, - output = { - (init to "L2") { - nop() - } - ("L2" to final) { - nop() - } - }, - ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - ErrorLocationPass(false) - ), - input = { - (init to final) { - "reach_error".invoke() - } - }, - output = { - (init to err) { skip() } - }, - ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - FinalLocationPass(false), - UnusedLocRemovalPass() - ), - input = { - (init to "L1") { - "abort".invoke() - } - (init to "L1") { skip() } - ("L1" to "L2") { - "exit".invoke() - } - }, - output = { - (init to final) { - assume("false") - } - (init to "L1") { skip() } - ("L1" to final) { - assume("false") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0"; }, - passes = listOf( - LoopUnrollPass() - ), - input = { - (init to "L1") { - "x".assign("0") - } - ("L1" to "L2") { - assume("(< x 3)") - "x".assign("(+ x 1)") - } - ("L2" to "L1") { - skip() - } - ("L1" to final) { - assume("(= x 3)") - } - }, - output = { - (init to "L1") { - "x".assign("0") - } - ("L1" to "L2_loop0") { - nop() - "x".assign("(+ x 1)") - } - ("L2_loop0" to "L1_loop0") { - skip() - } - ("L1_loop0" to "L2_loop1") { - nop() - "x".assign("(+ x 1)") - } - ("L2_loop1" to "L1_loop1") { - skip() - } - ("L1_loop1" to "L2_loop2") { - nop() - "x".assign("(+ x 1)") - } - ("L2_loop2" to "L1_loop2") { - skip() - } - ("L1_loop2" to final) { - nop() - } - }, - ), - PassTestData( - global = { - "y" type BvType(32) init "0" - ("x" type FpType(8, 24) init "0.0f").also { - fpParseContext.metadata.create(it.ref, "cType", CFloat(null, fpParseContext)) - }; - }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - FpFunctionsToExprsPass(fpParseContext), - ), - input = { - (init to final) { - "fabs".invoke("x", "x") - } - (init to final) { - "floor".invoke("x", "x") - } - (init to final) { - "fmax".invoke("x", "x", "x") - } - (init to final) { - "fmin".invoke("x", "x", "x") - } - (init to final) { - "sqrt".invoke("x", "x") - } - (init to final) { - "round".invoke("x", "x") - } - (init to final) { - "trunc".invoke("x", "x") - } - (init to final) { - "ceil".invoke("x", "x") - } - (init to final) { - "isinf".invoke("y", "x") - } - (init to final) { - "isfinite".invoke("y", "x") - } - }, - output = { - (init to final) { - "x".assign("(fpabs x)") - } - (init to final) { - "x".assign("(fproundtoint[RTN] x)") - } - (init to final) { - "x".assign("(fpmax x x)") - } - (init to final) { - "x".assign("(fpmin x x)") - } - (init to final) { - "x".assign("(fpsqrt x)") - } - (init to final) { - "x".assign("(fproundtoint[RNA] x)") - } - (init to final) { - "x".assign("(fproundtoint[RTZ] x)") - } - (init to final) { - "x".assign("(fproundtoint[RTP] x)") - } - (init to final) { - "y".assign( - "(ite (isinfinite x) #b00000000000000000000000000000001 #b00000000000000000000000000000000)") - } - (init to final) { - "y".assign( - "(ite (or (isinfinite x) (fpisnan x)) #b00000000000000000000000000000000 #b00000000000000000000000000000001)") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0"; "y" type Int() init "0"; }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - HavocPromotionAndRange(parseContext), - ), - input = { - (init to final) { - havoc("x") - "y".assign("x") - } - }, - output = { - (init to final) { - havoc("y") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0"; "y" type Int() init "0"; }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - HavocPromotionAndRange(parseContext), - ), - input = { - (init to final) { - havoc("x") - "y".assign("x").also { - parseContext.metadata.create( - ((it.labels.last() as StmtLabel).stmt as AssignStmt<*>).varDecl.ref, "cType", - CSignedInt(null, parseContext)) - } - } - }, - output = { - (init to final) { - havoc("y") - assume("(and (>= y -2147483648) (<= y 2147483647))") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0" }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - RemoveDeadEnds(), - UnusedLocRemovalPass() - ), - input = { - (init to "L1") { - assume("1 == 1") - } - (init to "L2") { - assume("1 == 1") - } - ("L2" to "L3") { - assume("false") - } - ("L3" to "L2") { - assume("false") - } - (init to "L3") { - "main"() - } - (init to "L3") { - "x".start("main") - } - }, - output = { - (init to "L3") { - "main"() - } - (init to "L3") { - "x".start("main") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0"; "thr1" type Int() init "0" }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - CLibraryFunctionsPass(), - ), - input = { - (init to "L1") { - "pthread_create"("0", "x", "0", "thr1", "0") - } - (init to "L2") { - "pthread_join"("0", "x") - } - (init to "L3") { - "pthread_mutex_lock"("0", "x") - } - (init to "L4") { - "pthread_mutex_unlock"("0", "x") - } - }, - output = { - (init to "L1") { - "x".start("thr1", "0", "0") - } - (init to "L2") { - "x".join() - } - (init to "L3") { - fence("mutex_lock(x)") - } - (init to "L4") { - fence("mutex_unlock(x)") - } - }, - ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - SvCompIntrinsicsPass(), - ), - input = { - (init to "L1") { - "__VERIFIER_atomic_begin"("0") - } - (init to "L2") { - "__VERIFIER_atomic_end"("0") - } - }, - output = { - (init to "L1") { - fence("ATOMIC_BEGIN") - } - (init to "L2") { - fence("ATOMIC_END") - } - }, - ), - PassTestData( - global = { "x" type Int() init "0"; "thr1" type Int() init "0" }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - NondetFunctionPass() - ), - input = { - (init to "L1") { - "__VERIFIER_nondet_int"("x") - } - }, - output = { - (init to "L1") { - havoc("x") - } - }, + @JvmStatic + val data: List = + listOf( + PassTestData( + global = { + "x" type Int() init "0" + "y" type Int() init "0" + }, + passes = listOf(NormalizePass(), DeterministicPass()), + input = { + (init to final) { + nondet { + havoc("x") + havoc("y") + } + } + }, + output = { + (init to final) { havoc("x") } + (init to final) { havoc("y") } + }, + ), + PassTestData( + global = {}, + passes = + listOf( + NormalizePass(), + DeterministicPass(), + EliminateSelfLoops(), + LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_SEQ }, ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - UnusedVarPass(NullLogger.getInstance()) - ), - input = { - "tmp" type Int() - }, - output = { - }, + input = { + (init to "L1") { assume("1 == 1") } + ("L1" to final) { assume("false") } + }, + output = { + (init to final) { + assume("1 == 1") + assume("false") + } + }, + ), + PassTestData( + global = {}, + passes = + listOf( + NormalizePass(), + DeterministicPass(), + EliminateSelfLoops(), + LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_FULL }, ), - PassTestData( - global = { }, - passes = listOf( - NormalizePass(), - DeterministicPass(), - EliminateSelfLoops() - ), - input = { - ("L1" to "L1") { - assume("1 == 1") - } - }, - output = null, + input = { + (init to "L1") { assume("1 == 1") } + ("L1" to "L2") { assume("1 == 1") } + ("L2" to final) { assume("false") } + ("L2" to final) { assume("1 == 2") } + }, + output = { + (init to final) { + assume("1 == 1") + assume("1 == 1") + nondet { + assume("false") + assume("1 == 2") + } + } + }, + ), + PassTestData( + global = {}, + passes = listOf(EmptyEdgeRemovalPass(), UnusedLocRemovalPass()), + input = { + (init to "L1") { nop() } + ("L1" to "L2") { nop() } + ("L2" to final) { nop() } + }, + output = { + (init to "L2") { nop() } + ("L2" to final) { nop() } + }, + ), + PassTestData( + global = {}, + passes = listOf(NormalizePass(), DeterministicPass(), ErrorLocationPass(false)), + input = { (init to final) { "reach_error".invoke() } }, + output = { (init to err) { skip() } }, + ), + PassTestData( + global = {}, + passes = + listOf( + NormalizePass(), + DeterministicPass(), + FinalLocationPass(false), + UnusedLocRemovalPass(), ), - ) - - } + input = { + (init to "L1") { "abort".invoke() } + (init to "L1") { skip() } + ("L1" to "L2") { "exit".invoke() } + }, + output = { + (init to final) { assume("false") } + (init to "L1") { skip() } + ("L1" to final) { assume("false") } + }, + ), + PassTestData( + global = { "x" type Int() init "0" }, + passes = listOf(LoopUnrollPass()), + input = { + (init to "L1") { "x".assign("0") } + ("L1" to "L2") { + assume("(< x 3)") + "x".assign("(+ x 1)") + } + ("L2" to "L1") { skip() } + ("L1" to final) { assume("(= x 3)") } + }, + output = { + (init to "L1") { "x".assign("0") } + ("L1" to "L2_loop0") { + nop() + "x".assign("(+ x 1)") + } + ("L2_loop0" to "L1_loop0") { skip() } + ("L1_loop0" to "L2_loop1") { + nop() + "x".assign("(+ x 1)") + } + ("L2_loop1" to "L1_loop1") { skip() } + ("L1_loop1" to "L2_loop2") { + nop() + "x".assign("(+ x 1)") + } + ("L2_loop2" to "L1_loop2") { skip() } + ("L1_loop2" to final) { nop() } + }, + ), + PassTestData( + global = { + "y" type BvType(32) init "0" + ("x" type FpType(8, 24) init "0.0f").also { + fpParseContext.metadata.create(it.ref, "cType", CFloat(null, fpParseContext)) + } + }, + passes = + listOf(NormalizePass(), DeterministicPass(), FpFunctionsToExprsPass(fpParseContext)), + input = { + (init to final) { "fabs".invoke("x", "x") } + (init to final) { "floor".invoke("x", "x") } + (init to final) { "fmax".invoke("x", "x", "x") } + (init to final) { "fmin".invoke("x", "x", "x") } + (init to final) { "sqrt".invoke("x", "x") } + (init to final) { "round".invoke("x", "x") } + (init to final) { "trunc".invoke("x", "x") } + (init to final) { "ceil".invoke("x", "x") } + (init to final) { "isinf".invoke("y", "x") } + (init to final) { "isfinite".invoke("y", "x") } + }, + output = { + (init to final) { "x".assign("(fpabs x)") } + (init to final) { "x".assign("(fproundtoint[RTN] x)") } + (init to final) { "x".assign("(fpmax x x)") } + (init to final) { "x".assign("(fpmin x x)") } + (init to final) { "x".assign("(fpsqrt x)") } + (init to final) { "x".assign("(fproundtoint[RNA] x)") } + (init to final) { "x".assign("(fproundtoint[RTZ] x)") } + (init to final) { "x".assign("(fproundtoint[RTP] x)") } + (init to final) { + "y" + .assign( + "(ite (isinfinite x) #b00000000000000000000000000000001 #b00000000000000000000000000000000)" + ) + } + (init to final) { + "y" + .assign( + "(ite (or (isinfinite x) (fpisnan x)) #b00000000000000000000000000000000 #b00000000000000000000000000000001)" + ) + } + }, + ), + PassTestData( + global = { + "x" type Int() init "0" + "y" type Int() init "0" + }, + passes = + listOf(NormalizePass(), DeterministicPass(), HavocPromotionAndRange(parseContext)), + input = { + (init to final) { + havoc("x") + "y".assign("x") + } + }, + output = { (init to final) { havoc("y") } }, + ), + PassTestData( + global = { + "x" type Int() init "0" + "y" type Int() init "0" + }, + passes = + listOf(NormalizePass(), DeterministicPass(), HavocPromotionAndRange(parseContext)), + input = { + (init to final) { + havoc("x") + "y".assign("x").also { + parseContext.metadata.create( + ((it.labels.last() as StmtLabel).stmt as AssignStmt<*>).varDecl.ref, + "cType", + CSignedInt(null, parseContext), + ) + } + } + }, + output = { + (init to final) { + havoc("y") + assume("(and (>= y -2147483648) (<= y 2147483647))") + } + }, + ), + PassTestData( + global = { "x" type Int() init "0" }, + passes = + listOf(NormalizePass(), DeterministicPass(), RemoveDeadEnds(), UnusedLocRemovalPass()), + input = { + (init to "L1") { assume("1 == 1") } + (init to "L2") { assume("1 == 1") } + ("L2" to "L3") { assume("false") } + ("L3" to "L2") { assume("false") } + (init to "L3") { "main"() } + (init to "L3") { "x".start("main") } + }, + output = { + (init to "L3") { "main"() } + (init to "L3") { "x".start("main") } + }, + ), + PassTestData( + global = { + "x" type Int() init "0" + "thr1" type Int() init "0" + }, + passes = listOf(NormalizePass(), DeterministicPass(), CLibraryFunctionsPass()), + input = { + (init to "L1") { "pthread_create"("x", "x", "0", "thr1", "0") } + (init to "L2") { "pthread_join"("x", "x") } + (init to "L3") { "pthread_mutex_lock"("0", "x") } + (init to "L4") { "pthread_mutex_unlock"("0", "x") } + }, + output = { + (init to "L1") { + "x".start("thr1", "0", "0") + "x".assign("0") + } + (init to "L2") { + "x".join() + "x".assign("0") + } + (init to "L3") { fence("mutex_lock(x)") } + (init to "L4") { fence("mutex_unlock(x)") } + }, + ), + PassTestData( + global = {}, + passes = listOf(NormalizePass(), DeterministicPass(), SvCompIntrinsicsPass()), + input = { + (init to "L1") { "__VERIFIER_atomic_begin"("0") } + (init to "L2") { "__VERIFIER_atomic_end"("0") } + }, + output = { + (init to "L1") { fence("ATOMIC_BEGIN") } + (init to "L2") { fence("ATOMIC_END") } + }, + ), + PassTestData( + global = { + "x" type Int() init "0" + "thr1" type Int() init "0" + }, + passes = listOf(NormalizePass(), DeterministicPass(), NondetFunctionPass()), + input = { (init to "L1") { "__VERIFIER_nondet_int"("x") } }, + output = { (init to "L1") { havoc("x") } }, + ), + PassTestData( + global = {}, + passes = + listOf(NormalizePass(), DeterministicPass(), UnusedVarPass(NullLogger.getInstance())), + input = { "tmp" type Int() }, + output = {}, + ), + PassTestData( + global = {}, + passes = listOf(NormalizePass(), DeterministicPass(), EliminateSelfLoops()), + input = { ("L1" to "L1") { assume("1 == 1") } }, + output = null, + ), + ) + } - @ParameterizedTest - @MethodSource("getData") - fun testPass(input: XcfaProcedureBuilder, output: XcfaProcedureBuilder?, - passes: List) { - println("Trying to run $passes on input...") - val actualOutput = passes.fold(input) { acc, procedurePass -> procedurePass.run(acc) } - .build(dummyXcfa) - if (output != null) { - val expectedOutput = output.build(dummyXcfa) - println("Expecting output:\t$expectedOutput\n Actual output:\t$actualOutput") - assertEquals(expectedOutput, actualOutput) - } - println(" Actual output:\t$actualOutput") - println("=============PASS=============\n") + @ParameterizedTest + @MethodSource("getData") + fun testPass( + input: XcfaProcedureBuilder, + output: XcfaProcedureBuilder?, + passes: List, + ) { + println("Trying to run $passes on input...") + val actualOutput = + passes.fold(input) { acc, procedurePass -> procedurePass.run(acc) }.build(dummyXcfa) + if (output != null) { + val expectedOutput = output.build(dummyXcfa) + println("Expecting output:\t$expectedOutput\n Actual output:\t$actualOutput") + assertEquals(expectedOutput, actualOutput) } + println(" Actual output:\t$actualOutput") + println("=============PASS=============\n") + } - @Test - fun testChangeVars() { - val x = Var("x", Int()) - val y = Var("y", Int()) - val xcfaLabel = { a: VarDecl, b: VarDecl -> - StmtLabel(Assign(a, b.ref)) - } + @Test + fun testChangeVars() { + val x = Var("x", Int()) + val y = Var("y", Int()) + val xcfaLabel = { a: VarDecl, b: VarDecl -> StmtLabel(Assign(a, b.ref)) } - val x_prime = Var("x'", Int()) - assertEquals(xcfaLabel(x, y), xcfaLabel(x, y).changeVars(emptyMap())) - assertEquals(xcfaLabel(x_prime, y), xcfaLabel(x, y).changeVars(mapOf(Pair(x, x_prime)))) - assertEquals(xcfaLabel(x, x_prime), xcfaLabel(x, y).changeVars(mapOf(Pair(y, x_prime)))) - } + val x_prime = Var("x'", Int()) + assertEquals(xcfaLabel(x, y), xcfaLabel(x, y).changeVars(emptyMap())) + assertEquals(xcfaLabel(x_prime, y), xcfaLabel(x, y).changeVars(mapOf(Pair(x, x_prime)))) + assertEquals(xcfaLabel(x, x_prime), xcfaLabel(x, y).changeVars(mapOf(Pair(y, x_prime)))) + } - @Test - fun testInline() { - val xcfaSource = xcfa("example") { - procedure("main", ProcedurePassManager(listOf( - NormalizePass(), - DeterministicPass(), - InlineProceduresPass(parseContext)))) { - (init to final) { - "proc1"() - } - } - procedure("proc1") { - (init to final) { - assume("1 == 1") - } - } + @Test + fun testInline() { + val xcfaSource = + xcfa("example") { + procedure( + "main", + ProcedurePassManager( + listOf(NormalizePass(), DeterministicPass(), InlineProceduresPass(parseContext)) + ), + ) { + (init to final) { "proc1"() } } + procedure("proc1") { (init to final) { assume("1 == 1") } } + } - assertTrue(xcfaSource.procedures.first { it.name == "main" }.edges.none { - it.getFlatLabels().any { it is InvokeLabel } - }) - } + assertTrue( + xcfaSource.procedures + .first { it.name == "main" } + .edges + .none { it.getFlatLabels().any { it is InvokeLabel } } + ) + } - @Test - fun testCPipeline() { - val xcfaSource = xcfa("example") { - procedure("main", CPasses(false, parseContext, NullLogger.getInstance())) { - (init to final) { - "proc1"() - } - } - procedure("proc1") { - (init to final) { - assume("1 == 1") - } - } + @Test + fun testCPipeline() { + val xcfaSource = + xcfa("example") { + procedure("main", CPasses(false, parseContext, NullLogger.getInstance())) { + (init to final) { "proc1"() } } + procedure("proc1") { (init to final) { assume("1 == 1") } } + } - assertTrue(xcfaSource.procedures.first { it.name == "main" }.edges.none { - it.getFlatLabels().any { it is InvokeLabel } - }) - } + assertTrue( + xcfaSource.procedures + .first { it.name == "main" } + .edges + .none { it.getFlatLabels().any { it is InvokeLabel } } + ) + } - @Test - fun testSplit() { - lateinit var edge: XcfaEdge - val xcfaSource = xcfa("example") { - procedure("main", CPasses(false, parseContext, NullLogger.getInstance())) { - edge = (init to final) { - assume("1 == 1") - "proc1"() - assume("1 == 1") - } - } + @Test + fun testSplit() { + lateinit var edge: XcfaEdge + val xcfaSource = + xcfa("example") { + procedure("main", CPasses(false, parseContext, NullLogger.getInstance())) { + edge = (init to final) { + assume("1 == 1") + "proc1"() + assume("1 == 1") + } } + } - val newEdges = edge.splitIf { it is InvokeLabel } - Assertions.assertTrue(newEdges.size == 3) - } - -} \ No newline at end of file + val newEdges = edge.splitIf { it is InvokeLabel } + assertTrue(newEdges.size == 3) + } +} diff --git a/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/UtilsTest.kt b/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/UtilsTest.kt index 77c48dc44e..ceac776dc8 100644 --- a/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/UtilsTest.kt +++ b/subprojects/xcfa/xcfa/src/test/java/hu/bme/mit/theta/xcfa/passes/UtilsTest.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.Decl @@ -30,42 +29,56 @@ import org.junit.jupiter.params.provider.MethodSource class UtilsTest { - companion object { - - private val x = Var("x", Int()) - private val y = Var("y", Int()) - private val xPrime = Var("x'", Int()) - private val map: Map, VarDecl<*>> = mapOf(Pair(x, xPrime)) - - @JvmStatic - fun getLabels(): List = listOf( - Arguments.of(InvokeLabel("", listOf(x.ref, y.ref), EmptyMetaData), - InvokeLabel("", listOf(xPrime.ref, y.ref), EmptyMetaData)), - Arguments.of(JoinLabel(x, EmptyMetaData), JoinLabel(xPrime, EmptyMetaData)), - Arguments.of(NondetLabel(setOf(NopLabel), EmptyMetaData), NondetLabel(setOf(NopLabel), EmptyMetaData)), - Arguments.of(SequenceLabel(listOf(NopLabel), EmptyMetaData), - SequenceLabel(listOf(NopLabel), EmptyMetaData)), - Arguments.of(ReadLabel(x, y, setOf(), EmptyMetaData), ReadLabel(xPrime, y, setOf(), EmptyMetaData)), - Arguments.of(WriteLabel(x, y, setOf(), EmptyMetaData), WriteLabel(xPrime, y, setOf(), EmptyMetaData)), - Arguments.of(FenceLabel(setOf(), EmptyMetaData), FenceLabel(setOf(), EmptyMetaData)), - Arguments.of(StartLabel("", listOf(x.ref), y, EmptyMetaData), - StartLabel("", listOf(xPrime.ref), y, EmptyMetaData)), - Arguments.of(ReturnLabel(JoinLabel(x, EmptyMetaData)), ReturnLabel(JoinLabel(xPrime, EmptyMetaData))), + companion object { - Arguments.of(StmtLabel(Assign(x, y.ref)), - StmtLabel(Assign(xPrime, y.ref))), - Arguments.of(StmtLabel(Havoc(x)), - StmtLabel(Havoc(xPrime))), - Arguments.of(StmtLabel(Assume(Eq(x.ref, y.ref))), - StmtLabel(Assume(Eq(xPrime.ref, y.ref)))), - Arguments.of(StmtLabel(Skip()), StmtLabel(Skip())), - ) - } + private val x = Var("x", Int()) + private val y = Var("y", Int()) + private val xPrime = Var("x'", Int()) + private val map: Map, VarDecl<*>> = mapOf(Pair(x, xPrime)) - @ParameterizedTest - @MethodSource("getLabels") - fun testChangeVars(labelIn: XcfaLabel, labelExp: XcfaLabel) { - Assertions.assertEquals(labelExp, labelIn.changeVars(map)) - } + @JvmStatic + fun getLabels(): List = + listOf( + Arguments.of( + InvokeLabel("", listOf(x.ref, y.ref), EmptyMetaData), + InvokeLabel("", listOf(xPrime.ref, y.ref), EmptyMetaData), + ), + Arguments.of(JoinLabel(x, EmptyMetaData), JoinLabel(xPrime, EmptyMetaData)), + Arguments.of( + NondetLabel(setOf(NopLabel), EmptyMetaData), + NondetLabel(setOf(NopLabel), EmptyMetaData), + ), + Arguments.of( + SequenceLabel(listOf(NopLabel), EmptyMetaData), + SequenceLabel(listOf(NopLabel), EmptyMetaData), + ), + Arguments.of( + ReadLabel(x, y, setOf(), EmptyMetaData), + ReadLabel(xPrime, y, setOf(), EmptyMetaData), + ), + Arguments.of( + WriteLabel(x, y, setOf(), EmptyMetaData), + WriteLabel(xPrime, y, setOf(), EmptyMetaData), + ), + Arguments.of(FenceLabel(setOf(), EmptyMetaData), FenceLabel(setOf(), EmptyMetaData)), + Arguments.of( + StartLabel("", listOf(x.ref), y, EmptyMetaData), + StartLabel("", listOf(xPrime.ref), y, EmptyMetaData), + ), + Arguments.of( + ReturnLabel(JoinLabel(x, EmptyMetaData)), + ReturnLabel(JoinLabel(xPrime, EmptyMetaData)), + ), + Arguments.of(StmtLabel(Assign(x, y.ref)), StmtLabel(Assign(xPrime, y.ref))), + Arguments.of(StmtLabel(Havoc(x)), StmtLabel(Havoc(xPrime))), + Arguments.of(StmtLabel(Assume(Eq(x.ref, y.ref))), StmtLabel(Assume(Eq(xPrime.ref, y.ref)))), + Arguments.of(StmtLabel(Skip()), StmtLabel(Skip())), + ) + } -} \ No newline at end of file + @ParameterizedTest + @MethodSource("getLabels") + fun testChangeVars(labelIn: XcfaLabel, labelExp: XcfaLabel) { + Assertions.assertEquals(labelExp, labelIn.changeVars(map)) + } +} diff --git a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt index 487a206379..19d0e67234 100644 --- a/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt +++ b/subprojects/xcfa/xcfa2chc/src/test/java/hu/bme/mit/theta/xcfa2chc/TestChcUtils.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xcfa2chc import hu.bme.mit.theta.common.OsHelper @@ -40,579 +39,758 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource private val iParamLut = LinkedHashMap>() -private fun iP(name: String) = iParamLut.getOrPut(name) { Decls.Param(name, IntExprs.Int()) } +private fun iP(name: String) = iParamLut.getOrPut(name) { Decls.Param(name, IntExprs.Int()) } class TestChcUtils { - companion object { - - private var solverManager: SmtLibSolverManager? = null - private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() - - private val SOLVERS: List> = listOf( - Pair("z3", "4.12.6"), - Pair("z3", "4.13.0"), - ) - - @JvmStatic - fun solvers(): List { - return SOLVERS.map { Arguments.of(it) } - } - - @BeforeAll - @JvmStatic - fun init() { - if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { - val home = SmtLibSolverManager.HOME + companion object { - solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) - for ((solver, version) in SOLVERS) { + private var solverManager: SmtLibSolverManager? = null + private val solverFactories: MutableMap, SolverFactory> = LinkedHashMap() - try { - solverManager!!.install(solver, version, version, null, false) - } catch (e: SmtLibSolverInstallerException) { - e.printStackTrace() - } + private val SOLVERS: List> = + listOf(Pair("z3", "4.12.6"), Pair("z3", "4.13.0")) - solverFactories.put(Pair(solver, version), solverManager!!.getSolverFactory(solver, version)) - } - } - } - - @AfterAll - @JvmStatic - fun destroy() { - for ((solver, version) in SOLVERS) { - try { - solverManager?.uninstall(solver, version) - } catch (e: SmtLibSolverInstallerException) { - e.printStackTrace() - } - } - } - } - - @BeforeEach - fun before() { - Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + @JvmStatic + fun solvers(): List { + return SOLVERS.map { Arguments.of(it) } } - @ParameterizedTest(name = "[{index}] {0}") - @MethodSource("solvers") - fun testPetersonManualCounting(name: Pair) { - val solverFactory = solverFactories[name]!! - val i2i = ArrayType.of(Int(), Int()) - - val pI = ParamHolder(Int()) - val pA = ParamHolder(i2i) - val br = pA[9] - val po = pA[10] - val co = pA[11] - val rf = pA[12] - val com = pA[13] - val prevW = pI[14] - val eid = pI[15] - val eid2 = pI[17] - val vid = pI[18] - val vid3 = pI[19] - val eid3 = pI[20] - val eid4 = pI[21] - val eid5 = pI[22] - val eid6 = pI[23] - val vid4 = pI[24] - val eid7 = pI[25] - val eid8 = pI[26] - val vid5 = pI[27] - val eid9 = pI[28] - val eid10 = pI[29] - val eid11 = pI[50] - - val turn = pI[30] - val flag1 = pI[31] - val flag2 = pI[32] - val cnt = pI[33] - val turn_old = pI[40] - val flag1_old = pI[41] - val flag2_old = pI[42] - val cnt_old = pI[43] - - val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com - - val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0CF = Relation("T0_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1CF = Relation("T1_critical_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Int(), Int(), Int(), - Int()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Int()) // br, co, rf, com, eid, vid, val - - // problem: unique rf values (W->R) will disable some possible reads - - init(br, co, rf, com, eid) += - Eq(eid, Int(0)) + - Eq(Read(co, Int(0)), Int(0)) + - Eq(Read(com, Int(0)), Int(0)) - - W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(0)) + - Eq(pI[0], Int(0)) - W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(1)) + - Eq(pI[0], Int(0)) - W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(2)) + - Eq(pI[0], Int(0)) - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(3)) + - Eq(pI[0], Int(0)) - - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += - init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += - init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) - - W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 1 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(1)) + - Eq(pI[0], Int(1)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 1 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(2)) + - Eq(pI[0], Int(1)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(0)) + - Eq(pI[0], Int(0)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pI[0]) += // turn := 1 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(0)) + - Eq(pI[0], Int(1)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source - Eq(Add(eid, Int(2)), eid9) + // eid update - Eq(vid3, Int(2)) + // flag[1] read - Eq(Read(rf, eid3), eid9) + // rf - Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) - Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) - Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 - Eq(pI[1], flag2) + - W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn - Eq(Add(eid, Int(4)), eid10) + // eid update - Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf - Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) - Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) - Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 - Eq(pI[2], turn) + - Or(Eq(turn, Int(0)), Eq(flag2, Int(0))) + - W(br, co, rf, com, eid7, vid5, pI[3]).expr + // turn - W(br, co, rf, com, eid8, vid5, pI[4]).expr + // turn - Eq(Add(eid, Int(6)), eid11) + // eid update - Eq(vid5, Int(3)) + // turn read - Eq(Read(rf, eid11), eid7) + // rf - Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) - Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) - Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 - Eq(pI[3], cnt) + - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt_old).expr + // previous loc - Eq(Add(eid, Int(8)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) - Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) - Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) - Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) - - T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source - Eq(Add(eid, Int(2)), eid9) + // eid update - Eq(vid3, Int(1)) + // flag[0] read - Eq(Read(rf, eid9), eid3) + // rf - Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) - Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) - Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 - Eq(pI[1], flag1) + - W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn - Eq(Add(eid, Int(4)), eid10) + // eid update - Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf - Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) - Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) - Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 - Eq(pI[2], turn) + - Or(Eq(turn, Int(1)), Eq(flag1, Int(0))) + - W(br, co, rf, com, eid7, vid5, pI[3]).expr + // cnt - W(br, co, rf, com, eid8, vid5, pI[4]).expr + // cnt - Eq(Add(eid, Int(6)), eid11) + // eid update - Eq(vid5, Int(3)) + // turn - Eq(Read(rf, eid11), eid7) + // rf - Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) - Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) - Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 - Eq(pI[3], cnt) + - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt_old).expr + // previous loc - Eq(Add(eid, Int(8)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) - Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) - Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) - Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(3)) + - Eq(pI[0], Add(cnt, Int(1))) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(3)) + - Eq(pI[0], Add(cnt, Int(1))) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(3)) + - Eq(pI[0], Int(0)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(3)) + - Eq(pI[0], Int(0)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - - - T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(1)) + - Eq(pI[0], Int(0)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pI[0]) += // flag2 := 0 - W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW - T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(2)) + - Eq(pI[0], Int(0)) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write - T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) - !(T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) - - val relations = listOf(init, T0, T0G, T0C, T0CF, T0F, T1, T1G, T1C, T1CF, T1F, W) - val expr = relations.toSMT2() - println(expr) - val solver = solverFactory.createHornSolver() - solver.add(relations) - solver.check() - Assertions.assertTrue(solver.status == SolverStatus.SAT) + @BeforeAll + @JvmStatic + fun init() { + if (OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) { + val home = SmtLibSolverManager.HOME + + solverManager = SmtLibSolverManager.create(home, NullLogger.getInstance()) + for ((solver, version) in SOLVERS) { + + try { + solverManager!!.install(solver, version, version, null, false) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + + solverFactories.put( + Pair(solver, version), + solverManager!!.getSolverFactory(solver, version), + ) + } + } } - @ParameterizedTest(name = "[{index}] {0}") - @MethodSource("solvers") - fun testPetersonNoCounting(name: Pair) { - val solverFactory = solverFactories[name]!! - val i2i = ArrayType.of(Int(), Int()) - - val pI = ParamHolder(Int()) - val pB = ParamHolder(Bool()) - val pA = ParamHolder(i2i) - val br = pA[9] - val po = pA[10] - val co = pA[11] - val rf = pA[12] - val com = pA[13] - val prevW = pI[14] - val eid = pI[15] - val eid2 = pI[17] - val vid = pI[18] - val vid3 = pI[19] - val eid3 = pI[20] - val eid4 = pI[21] - val eid5 = pI[22] - val eid6 = pI[23] - val vid4 = pI[24] - val eid7 = pI[25] - val eid8 = pI[26] - val vid5 = pI[27] - val eid9 = pI[28] - val eid10 = pI[29] - val eid11 = pI[50] - - val turn = pB[30] - val flag1 = pB[31] - val flag2 = pB[32] - val cnt = pB[33] - val turn_old = pB[40] - val flag1_old = pB[41] - val flag2_old = pB[42] - - val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com - - val T0 = Relation("T0", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0G = Relation("T0_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0C = Relation("T0_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T0F = Relation("T0_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val T1 = Relation("T1", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1G = Relation("T1_gate", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1C = Relation("T1_critical", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - val T1F = Relation("T1_final", i2i, i2i, i2i, i2i, Int(), Bool(), Bool(), Bool(), - Bool()) // br, co, rf, com, eid, turn, flag0, flag1, cnt - - val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Bool()) // br, co, rf, com, eid, vid, val - - init(br, co, rf, com, eid) += - Eq(eid, Int(0)) + - Eq(Read(co, Int(0)), Int(0)) + - Eq(Read(com, Int(0)), Int(0)) - - W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(0)) + - Eq(pB[0], False()) - W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(1)) + - Eq(pB[0], False()) - W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 0 - init(br, co, rf, com, eid).expr + - Eq(vid, Int(2)) + - Eq(pB[0], False()) - - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += - init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += - init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) - - W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 1 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(1)) + - Eq(pB[0], True()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 1 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(2)) + - Eq(pB[0], True()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write - T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write - T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pB[0]) += // turn := 1 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(0)) + - Eq(pB[0], True()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(0)) + - Eq(pB[0], False()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source - Eq(Add(eid, Int(2)), eid9) + // eid update - Eq(vid3, Int(2)) + // flag[1] read - Eq(Read(rf, eid3), eid9) + // rf - Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) - Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) - Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 - Eq(pB[1], flag2) + - W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn - Eq(Add(eid, Int(4)), eid10) + // eid update - Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf - Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) - Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) - Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 - Eq(pB[2], turn) + - Or(Not(turn), Not(flag2)) + - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write - T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt).expr + // previous loc - Eq(Add(eid, Int(6)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) - Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) - Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) - - T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source - W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source - Eq(Add(eid, Int(2)), eid9) + // eid update - Eq(vid3, Int(1)) + // flag[0] read - Eq(Read(rf, eid9), eid3) + // rf - Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) - Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) - Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 - Eq(pB[1], flag1) + - W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn - W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn - Eq(Add(eid, Int(4)), eid10) + // eid update - Eq(vid4, Int(0)) + // turn read - Eq(Read(rf, eid10), eid5) + // rf - Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) - Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) - Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 - Eq(pB[2], turn) + - Or(turn, Not(flag1)) + - W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write - T1G(br, co, rf, com, eid, turn_old, flag1_old, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(6)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) - Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) - Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) - - T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += - T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc - Eq(Add(eid, Int(2)), eid2) + // eid update - Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) - - W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(1)) + - Eq(pB[0], False()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 - W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW - T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + - Eq(vid, Int(2)) + - Eq(pB[0], False()) + - Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next - Lt(Read(com, eid2), Read(com, eid)) - - !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with - T1C(br, co, rf, com, eid2, turn_old, flag1_old, flag2_old, cnt).expr + - Eq(Read(com, eid), Read(com, eid2))) - - val relations = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W) - val expr = relations.toSMT2() - println(expr) - val solver = solverFactory.createHornSolver() - solver.add(relations) - solver.check() - Assertions.assertTrue(solver.status == SolverStatus.SAT) + @AfterAll + @JvmStatic + fun destroy() { + for ((solver, version) in SOLVERS) { + try { + solverManager?.uninstall(solver, version) + } catch (e: SmtLibSolverInstallerException) { + e.printStackTrace() + } + } } - + } + + @BeforeEach + fun before() { + Assumptions.assumeTrue(OsHelper.getOs() == OsHelper.OperatingSystem.LINUX) + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testPetersonManualCounting(name: Pair) { + val solverFactory = solverFactories[name]!! + val i2i = ArrayType.of(Int(), Int()) + + val pI = ParamHolder(Int()) + val pA = ParamHolder(i2i) + val br = pA[9] + val po = pA[10] + val co = pA[11] + val rf = pA[12] + val com = pA[13] + val prevW = pI[14] + val eid = pI[15] + val eid2 = pI[17] + val vid = pI[18] + val vid3 = pI[19] + val eid3 = pI[20] + val eid4 = pI[21] + val eid5 = pI[22] + val eid6 = pI[23] + val vid4 = pI[24] + val eid7 = pI[25] + val eid8 = pI[26] + val vid5 = pI[27] + val eid9 = pI[28] + val eid10 = pI[29] + val eid11 = pI[50] + + val turn = pI[30] + val flag1 = pI[31] + val flag2 = pI[32] + val cnt = pI[33] + val turn_old = pI[40] + val flag1_old = pI[41] + val flag2_old = pI[42] + val cnt_old = pI[43] + + val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com + + val T0 = + Relation( + "T0", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = + Relation( + "T0_gate", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = + Relation( + "T0_critical", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0CF = + Relation( + "T0_critical_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = + Relation( + "T0_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = + Relation( + "T1", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = + Relation( + "T1_gate", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = + Relation( + "T1_critical", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1CF = + Relation( + "T1_critical_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = + Relation( + "T1_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Int(), + Int(), + Int(), + Int(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val W = Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Int()) // br, co, rf, com, eid, vid, val + + // problem: unique rf values (W->R) will disable some possible reads + + init(br, co, rf, com, eid) += + Eq(eid, Int(0)) + Eq(Read(co, Int(0)), Int(0)) + Eq(Read(com, Int(0)), Int(0)) + + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(0)) + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(1)) + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(2)) + Eq(pI[0], Int(0)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(3)) + Eq(pI[0], Int(0)) + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) + + W(br, co, rf, com, eid, vid, pI[0]) += // flag0 := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // turn := 1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pI[0], Int(1)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(2)) + // flag[1] read + Eq(Read(rf, eid3), eid9) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pI[1], flag2) + + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pI[2], turn) + + Or(Eq(turn, Int(0)), Eq(flag2, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // turn + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // turn + Eq(Add(eid, Int(6)), eid11) + // eid update + Eq(vid5, Int(3)) + // turn read + Eq(Read(rf, eid11), eid7) + // rf + Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) + Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) + Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 + Eq(pI[3], cnt) + + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt_old).expr + // previous loc + Eq(Add(eid, Int(8)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) + Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) + + T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pI[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pI[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(1)) + // flag[0] read + Eq(Read(rf, eid9), eid3) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pI[1], flag1) + + W(br, co, rf, com, eid5, vid4, pI[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pI[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pI[2], turn) + + Or(Eq(turn, Int(1)), Eq(flag1, Int(0))) + + W(br, co, rf, com, eid7, vid5, pI[3]).expr + // cnt + W(br, co, rf, com, eid8, vid5, pI[4]).expr + // cnt + Eq(Add(eid, Int(6)), eid11) + // eid update + Eq(vid5, Int(3)) + // turn + Eq(Read(rf, eid11), eid7) + // rf + Lt(Read(com, eid7), Read(com, eid11)) + // com constraint (because rf) + Lt(Read(com, eid11), Read(com, eid8)) + // com constraint (because fr) + Eq(Add(Read(co, eid7), Int(1)), Read(co, eid8)) + // co-after is eid8 + Eq(pI[3], cnt) + + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt_old).expr + // previous loc + Eq(Add(eid, Int(8)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid11)) + // com constraint (because po) + Lt(Read(com, eid11), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Add(cnt, Int(1))) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := cnt+1 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Add(cnt, Int(1))) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1CF(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(3)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1CF(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pI[0]) += // flag1 := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pI[0]) += // flag2 := 0 + W(br, co, rf, com, eid2, vid, pI[1]).expr + // prevW + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pI[0], Int(0)) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pI[0]).expr + // successful write + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) + !(T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with Eq(cnt, Int(1))) + + val relations = listOf(init, T0, T0G, T0C, T0CF, T0F, T1, T1G, T1C, T1CF, T1F, W) + val expr = relations.toSMT2() + println(expr) + val solver = solverFactory.createHornSolver() + solver.add(relations) + solver.check() + Assertions.assertTrue(solver.status == SolverStatus.SAT) + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("solvers") + fun testPetersonNoCounting(name: Pair) { + val solverFactory = solverFactories[name]!! + val i2i = ArrayType.of(Int(), Int()) + + val pI = ParamHolder(Int()) + val pB = ParamHolder(Bool()) + val pA = ParamHolder(i2i) + val br = pA[9] + val po = pA[10] + val co = pA[11] + val rf = pA[12] + val com = pA[13] + val prevW = pI[14] + val eid = pI[15] + val eid2 = pI[17] + val vid = pI[18] + val vid3 = pI[19] + val eid3 = pI[20] + val eid4 = pI[21] + val eid5 = pI[22] + val eid6 = pI[23] + val vid4 = pI[24] + val eid7 = pI[25] + val eid8 = pI[26] + val vid5 = pI[27] + val eid9 = pI[28] + val eid10 = pI[29] + val eid11 = pI[50] + + val turn = pB[30] + val flag1 = pB[31] + val flag2 = pB[32] + val cnt = pB[33] + val turn_old = pB[40] + val flag1_old = pB[41] + val flag2_old = pB[42] + + val init = Relation("init", i2i, i2i, i2i, i2i, Int()) // br, co, rf, com + + val T0 = + Relation( + "T0", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0G = + Relation( + "T0_gate", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0C = + Relation( + "T0_critical", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T0F = + Relation( + "T0_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val T1 = + Relation( + "T1", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1G = + Relation( + "T1_gate", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1C = + Relation( + "T1_critical", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + val T1F = + Relation( + "T1_final", + i2i, + i2i, + i2i, + i2i, + Int(), + Bool(), + Bool(), + Bool(), + Bool(), + ) // br, co, rf, com, eid, turn, flag0, flag1, cnt + + val W = + Relation("W", i2i, i2i, i2i, i2i, Int(), Int(), Bool()) // br, co, rf, com, eid, vid, val + + init(br, co, rf, com, eid) += + Eq(eid, Int(0)) + Eq(Read(co, Int(0)), Int(0)) + Eq(Read(com, Int(0)), Int(0)) + + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(0)) + Eq(pB[0], False()) + W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(1)) + Eq(pB[0], False()) + W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 0 + init(br, co, rf, com, eid).expr + Eq(vid, Int(2)) + Eq(pB[0], False()) + + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(1)) + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt) += + init(br, co, rf, com, eid2).expr + Eq(eid, Int(2)) + + W(br, co, rf, com, eid, vid, pB[0]) += // flag0 := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // flag1 := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T0(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1G(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T1(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 1 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pB[0], True()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // turn := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1G(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(0)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + T0C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(2)) + // flag[1] read + Eq(Read(rf, eid3), eid9) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pB[1], flag2) + + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pB[2], turn) + + Or(Not(turn), Not(flag2)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T0G(br, co, rf, com, eid, turn_old, flag1, flag2_old, cnt).expr + // previous loc + Eq(Add(eid, Int(6)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) + + T1C(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + W(br, co, rf, com, eid3, vid3, pB[1]).expr + // rf-source + W(br, co, rf, com, eid4, vid3, pB[2]).expr + // rf-source + Eq(Add(eid, Int(2)), eid9) + // eid update + Eq(vid3, Int(1)) + // flag[0] read + Eq(Read(rf, eid9), eid3) + // rf + Lt(Read(com, eid3), Read(com, eid9)) + // com constraint (because rf) + Lt(Read(com, eid9), Read(com, eid4)) + // com constraint (because fr) + Eq(Add(Read(co, eid3), Int(1)), Read(co, eid4)) + // co-after is eid4 + Eq(pB[1], flag1) + + W(br, co, rf, com, eid5, vid4, pB[2]).expr + // turn + W(br, co, rf, com, eid6, vid4, pB[3]).expr + // turn + Eq(Add(eid, Int(4)), eid10) + // eid update + Eq(vid4, Int(0)) + // turn read + Eq(Read(rf, eid10), eid5) + // rf + Lt(Read(com, eid5), Read(com, eid10)) + // com constraint (because rf) + Lt(Read(com, eid10), Read(com, eid6)) + // com constraint (because fr) + Eq(Add(Read(co, eid5), Int(1)), Read(co, eid6)) + // co-after is eid6 + Eq(pB[2], turn) + + Or(turn, Not(flag1)) + + W(br, co, rf, com, eid, vid, pB[0]).expr + // successful write + T1G(br, co, rf, com, eid, turn_old, flag1_old, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(6)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid9)) + // com constraint (because po) + Lt(Read(com, eid9), Read(com, eid10)) + // com constraint (because po) + Lt(Read(com, eid10), Read(com, eid2)) // com constraint (because po) + + T0F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + T1F(br, co, rf, com, eid2, turn, flag1, flag2, cnt) += + T1C(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + // previous loc + Eq(Add(eid, Int(2)), eid2) + // eid update + Lt(Read(com, eid), Read(com, eid2)) // com constraint (because po) + + W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T0F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(1)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + W(br, co, rf, com, eid, vid, pB[0]) += // cnt := 0 + W(br, co, rf, com, eid2, vid, pB[1]).expr + // prevW + T1F(br, co, rf, com, eid, turn, flag1, flag2, cnt).expr + + Eq(vid, Int(2)) + + Eq(pB[0], False()) + + Eq(Add(Read(co, eid2), Int(1)), Read(co, eid)) + // co-next + Lt(Read(com, eid2), Read(com, eid)) + + !(T0C(br, co, rf, com, eid, turn, flag1, flag2, cnt) with + T1C(br, co, rf, com, eid2, turn_old, flag1_old, flag2_old, cnt).expr + + Eq(Read(com, eid), Read(com, eid2))) + + val relations = listOf(init, T0, T0G, T0C, T0F, T1, T1G, T1C, T1F, W) + val expr = relations.toSMT2() + println(expr) + val solver = solverFactory.createHornSolver() + solver.add(relations) + solver.check() + Assertions.assertTrue(solver.status == SolverStatus.SAT) + } } diff --git a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/XstsToRelations.kt b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/XstsToRelations.kt index 4e4a63908b..bae9dee270 100644 --- a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/XstsToRelations.kt +++ b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/XstsToRelations.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.analysis import hu.bme.mit.theta.core.Relation @@ -44,102 +43,111 @@ import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory import hu.bme.mit.theta.xsts.XSTS import java.math.BigInteger - fun XSTS.toRelations(): List { - val enumValueLut = LinkedHashMap>() - val varChangeMap = LinkedHashMap, VarDecl<*>>() - var cnt = 0 - fun Expr<*>.replaceEnums(): Expr<*> { - if (type is EnumType) { - when (this) { - is EnumLitExpr -> { - val value = - enumValueLut.computeIfAbsent(this.type) { LinkedHashMap() }.computeIfAbsent( - this.value) { cnt++ } - return IntLitExpr.of(BigInteger.valueOf(value.toLong())) - } - - is RefExpr -> { - return varChangeMap.computeIfAbsent(decl as VarDecl) { Var(this.decl.name, Int()) }.ref - } - - is PrimeExpr -> { - return PrimeExpr.of(op.replaceEnums()) - } - - else -> error("I expect that only EnumLiterals and RefExprs may have EnumType") - } - } else if (this is EnumEqExpr) { - return IntEqExpr.of(leftOp.replaceEnums() as Expr, rightOp.replaceEnums() as Expr); - } else if (this is EnumNeqExpr) { - return IntNeqExpr.of(leftOp.replaceEnums() as Expr, rightOp.replaceEnums() as Expr); - } else { - return this.map(Expr<*>::replaceEnums) + val enumValueLut = LinkedHashMap>() + val varChangeMap = LinkedHashMap, VarDecl<*>>() + var cnt = 0 + fun Expr<*>.replaceEnums(): Expr<*> { + if (type is EnumType) { + when (this) { + is EnumLitExpr -> { + val value = + enumValueLut + .computeIfAbsent(this.type) { LinkedHashMap() } + .computeIfAbsent(this.value) { cnt++ } + return IntLitExpr.of(BigInteger.valueOf(value.toLong())) + } + + is RefExpr -> { + return varChangeMap.computeIfAbsent(decl as VarDecl) { Var(this.decl.name, Int()) }.ref } - } - fun VarIndexing.replaceEnums() = transform().copyVars(varChangeMap).build() + is PrimeExpr -> { + return PrimeExpr.of(op.replaceEnums()) + } + else -> error("I expect that only EnumLiterals and RefExprs may have EnumType") + } + } else if (this is EnumEqExpr) { + return IntEqExpr.of( + leftOp.replaceEnums() as Expr, + rightOp.replaceEnums() as Expr, + ) + } else if (this is EnumNeqExpr) { + return IntNeqExpr.of( + leftOp.replaceEnums() as Expr, + rightOp.replaceEnums() as Expr, + ) + } else { + return this.map(Expr<*>::replaceEnums) + } + } - val initUnfoldResult = StmtUtils.toExpr(this.init, VarIndexingFactory.indexing(0)) - val initExpr = And(And(initUnfoldResult.exprs), this.initFormula).replaceEnums() as Expr - val initOffsetIndex = initUnfoldResult.indexing.replaceEnums() + fun VarIndexing.replaceEnums() = transform().copyVars(varChangeMap).build() - val envUnfoldResult = StmtUtils.toExpr(env, VarIndexingFactory.indexing(0)); - val envExpr = And(envUnfoldResult.exprs).replaceEnums() as Expr - val envOffsetIndex = envUnfoldResult.indexing.replaceEnums() + val initUnfoldResult = StmtUtils.toExpr(this.init, VarIndexingFactory.indexing(0)) + val initExpr = And(And(initUnfoldResult.exprs), this.initFormula).replaceEnums() as Expr + val initOffsetIndex = initUnfoldResult.indexing.replaceEnums() - val tranUnfoldResult = StmtUtils.toExpr(tran, VarIndexingFactory.indexing(0)); - val tranExpr = And(tranUnfoldResult.exprs).replaceEnums() as Expr - val tranOffsetIndex = tranUnfoldResult.indexing.replaceEnums() + val envUnfoldResult = StmtUtils.toExpr(env, VarIndexingFactory.indexing(0)) + val envExpr = And(envUnfoldResult.exprs).replaceEnums() as Expr + val envOffsetIndex = envUnfoldResult.indexing.replaceEnums() - val propExpr = this.prop.replaceEnums() as Expr + val tranUnfoldResult = StmtUtils.toExpr(tran, VarIndexingFactory.indexing(0)) + val tranExpr = And(tranUnfoldResult.exprs).replaceEnums() as Expr + val tranOffsetIndex = tranUnfoldResult.indexing.replaceEnums() - val vars = vars.map { varChangeMap[it] ?: it } + val propExpr = this.prop.replaceEnums() as Expr - val types = vars.map { it.type }.toTypedArray() - val oldParams = vars.associateWith { Param("|" + it.name + "|", it.type) } - val oldParamList = vars.map { oldParams[it]!!.ref }.toTypedArray() - val newParams = vars.associateWith { Param("|" + it.name + "_new|", it.type) } + val vars = vars.map { varChangeMap[it] ?: it } - fun toRelation( - rel: Relation, - prevRels: List, - notUnfoldedExpr: Expr, - indexing: VarIndexing, - vars: List> - ): Relation { + val types = vars.map { it.type }.toTypedArray() + val oldParams = vars.associateWith { Param("|" + it.name + "|", it.type) } + val oldParamList = vars.map { oldParams[it]!!.ref }.toTypedArray() + val newParams = vars.associateWith { Param("|" + it.name + "_new|", it.type) } - val expr = PathUtils.unfold(notUnfoldedExpr, VarIndexingFactory.indexing(0)) + fun toRelation( + rel: Relation, + prevRels: List, + notUnfoldedExpr: Expr, + indexing: VarIndexing, + vars: List>, + ): Relation { - // var[0] is oldParam, var[-1]is newParam, everything else is a fresh param - var cnt = 0 - val consts = ExprUtils.getIndexedConstants(expr).associateWith { - if (it.index == 0 && oldParams.containsKey(it.varDecl)) oldParams[it.varDecl]!! - else if (it.index == indexing[it.varDecl] && newParams.containsKey(it.varDecl)) newParams[it.varDecl]!! - else Param("__tmp_${cnt++}", it.type) - } - val newParamList = vars.map { if (indexing[it] == 0) oldParams[it]!!.ref else newParams[it]!!.ref } - .toTypedArray() - val paramdExpr = ExprUtils.changeDecls(expr, consts) - if (prevRels.isEmpty()) { - rel(*newParamList) += paramdExpr - } else { - for (prevRel in prevRels) { - rel(*newParamList) += prevRel(*oldParamList).expr + paramdExpr - } - } - return rel + val expr = PathUtils.unfold(notUnfoldedExpr, VarIndexingFactory.indexing(0)) + + // var[0] is oldParam, var[-1]is newParam, everything else is a fresh param + var cnt = 0 + val consts = + ExprUtils.getIndexedConstants(expr).associateWith { + if (it.index == 0 && oldParams.containsKey(it.varDecl)) oldParams[it.varDecl]!! + else if (it.index == indexing[it.varDecl] && newParams.containsKey(it.varDecl)) + newParams[it.varDecl]!! + else Param("__tmp_${cnt++}", it.type) + } + val newParamList = + vars + .map { if (indexing[it] == 0) oldParams[it]!!.ref else newParams[it]!!.ref } + .toTypedArray() + val paramdExpr = ExprUtils.changeDecls(expr, consts) + if (prevRels.isEmpty()) { + rel(*newParamList) += paramdExpr + } else { + for (prevRel in prevRels) { + rel(*newParamList) += prevRel(*oldParamList).expr + paramdExpr + } } + return rel + } - val init = Relation("init", *types) - val env = Relation("env", *types) - val tran = Relation("tran", *types) + val init = Relation("init", *types) + val env = Relation("env", *types) + val tran = Relation("tran", *types) - toRelation(init, listOf(), initExpr, initOffsetIndex, vars.toList()) - toRelation(env, listOf(init, tran), envExpr, envOffsetIndex, vars.toList()) - toRelation(tran, listOf(env), tranExpr, tranOffsetIndex, vars.toList()) - !(tran(*oldParamList) with ExprUtils.changeDecls(Not(propExpr), oldParams)) + toRelation(init, listOf(), initExpr, initOffsetIndex, vars.toList()) + toRelation(env, listOf(init, tran), envExpr, envOffsetIndex, vars.toList()) + toRelation(tran, listOf(env), tranExpr, tranOffsetIndex, vars.toList()) + !(tran(*oldParamList) with ExprUtils.changeDecls(Not(propExpr), oldParams)) - return listOf(init, tran, env) -} \ No newline at end of file + return listOf(init, tran, env) +} diff --git a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/RandomXsts.kt b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/RandomXsts.kt index a78b5bb70c..071890cd0c 100644 --- a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/RandomXsts.kt +++ b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/RandomXsts.kt @@ -29,310 +29,332 @@ import hu.bme.mit.theta.xsts.XSTS import kotlin.random.Random @JvmOverloads -fun generateXsts(seed: Int, numCtrl: Int = 1, numClock: Int = 1, numOther: Int = 3, - requireAllVarsWritten: Boolean = false): XSTS { - val writtenVars = object : StmtVisitor>, Set>> { - override fun visit(stmt: SkipStmt?, param: Set>): Set> { - return param - } - - override fun visit(stmt: AssumeStmt?, param: Set>): Set> { - return param - } - - override fun visit(stmt: AssignStmt, param: Set>): Set> { - return setOf(stmt.varDecl) + param - } - - override fun visit(stmt: HavocStmt, param: Set>): Set> { - return setOf(stmt.varDecl) + param - } - - override fun visit(stmt: SequenceStmt, param: Set>): Set> { - var res = param - for (s in stmt.stmts) { - res = s.accept(this, setOf()) - } - return res - } - - override fun visit(stmt: NonDetStmt, param: Set>): Set> { - var res = param - for (s in stmt.stmts) { - res = s.accept(this, setOf()) - } - return res - } - - override fun visit(stmt: OrtStmt?, param: Set>?): Set> { - TODO("Not yet implemented") - } - - override fun visit(stmt: LoopStmt, param: Set>): Set> { - return stmt.stmt.accept(this, param) - } - - override fun visit(stmt: IfStmt, param: Set>): Set> { - return stmt.then.accept(this, stmt.elze.accept(this, param)) +fun generateXsts( + seed: Int, + numCtrl: Int = 1, + numClock: Int = 1, + numOther: Int = 3, + requireAllVarsWritten: Boolean = false, +): XSTS { + val writtenVars = + object : StmtVisitor>, Set>> { + override fun visit(stmt: SkipStmt?, param: Set>): Set> { + return param + } + + override fun visit(stmt: AssumeStmt?, param: Set>): Set> { + return param + } + + override fun visit( + stmt: AssignStmt, + param: Set>, + ): Set> { + return setOf(stmt.varDecl) + param + } + + override fun visit( + stmt: HavocStmt, + param: Set>, + ): Set> { + return setOf(stmt.varDecl) + param + } + + override fun visit(stmt: SequenceStmt, param: Set>): Set> { + var res = param + for (s in stmt.stmts) { + res = s.accept(this, setOf()) } + return res + } - override fun visit( - stmt: MemoryAssignStmt?, param: Set>?): Set> { - TODO("Not yet implemented") + override fun visit(stmt: NonDetStmt, param: Set>): Set> { + var res = param + for (s in stmt.stmts) { + res = s.accept(this, setOf()) } - + return res + } + + override fun visit(stmt: OrtStmt?, param: Set>?): Set> { + TODO("Not yet implemented") + } + + override fun visit(stmt: LoopStmt, param: Set>): Set> { + return stmt.stmt.accept(this, param) + } + + override fun visit(stmt: IfStmt, param: Set>): Set> { + return stmt.then.accept(this, stmt.elze.accept(this, param)) + } + + override fun visit( + stmt: MemoryAssignStmt?, + param: Set>?, + ): Set> { + TODO("Not yet implemented") + } } - val all = numCtrl + numOther + numClock - val xsts = RandomXsts(seed, 3).generateRandomXsts(10, numCtrl, numClock, numOther) { - if (requireAllVarsWritten) { - val decls = it.tran.accept(writtenVars, setOf()) - decls.size == all // all vars are written - } else { - true - } + val all = numCtrl + numOther + numClock + val xsts = + RandomXsts(seed, 3).generateRandomXsts(10, numCtrl, numClock, numOther) { + if (requireAllVarsWritten) { + val decls = it.tran.accept(writtenVars, setOf()) + decls.size == all // all vars are written + } else { + true + } } - return xsts + return xsts } class RandomXsts(seed: Int, val exprMaxDepth: Int) { - lateinit var ctrlVars: List> - lateinit var otherVars: List> - lateinit var boolVars: List> - lateinit var clockVars: List> - lateinit var intVars: List> - - val random = Random(seed) - - fun generateRandomXsts( - depth: Int, - numCtrl: Int, - numClock: Int, - numOther: Int, - constraint: (XSTS) -> Boolean - ): XSTS { - var xsts: XSTS - do { - val trans = generateRandomStmt(depth, numCtrl, numClock, numOther) - val env = Stmts.NonDetStmt(listOf(Stmts.Skip())) - val init = Stmts.NonDetStmt(listOf(Stmts.Skip())) - val initExpr = BoolExprs.And( - ctrlVars.map { IntExprs.Eq(it.ref, IntExprs.Int(0)) } - + boolVars.map { BoolExprs.Not(it.ref) } - + intVars.map { IntExprs.Eq(it.ref, IntExprs.Int(0)) } - ) - var expr: Expr - do expr = randomBoolExpr(0) - while (ExprUtils.getVars(expr).isEmpty()) - xsts = XSTS( - ctrlVars.toSet(), - init, NonDetStmt.of(listOf(trans)), env, initExpr, expr - ) - } while (!(constraint(xsts))) - return xsts - } - - fun generateRandomStmt(depth: Int, numCtrl: Int, numClock: Int, numOther: Int): Stmt { - var i = 2 - ctrlVars = Array(numCtrl) { Decls.Var("ctlr${i++}", IntType.getInstance()) }.toList() - do { - otherVars = listOf( - Decls.Var("var0", IntType.getInstance()) as VarDecl, - Decls.Var("var1", BoolType.getInstance()) as VarDecl, - ) + Array(numOther - 2) { randomVar("var${i++}") }.toList() - } while (otherVars.all { it.type is IntType } || otherVars.all { it.type is BoolType }) - boolVars = otherVars.filter { it.type is BoolType }.map { it as VarDecl } - intVars = otherVars.filter { it.type is IntType }.map { it as VarDecl } - clockVars = Array(numClock) { Decls.Var("clock${i++}", IntType.getInstance()) }.toList() - - return randomIntermediate(0, depth) - } - - fun randomVar(name: String): VarDecl { - return listOf( - { Decls.Var(name, IntType.getInstance()) as VarDecl }, - { Decls.Var(name, BoolType.getInstance()) as VarDecl } - ).random(random)() - } - - fun randomIntermediate(currDepth: Int, maxDepth: Int): Stmt { - if (currDepth == maxDepth) return randomLeaf() - return listOf( - { randomLeaf() }, - { randomSeq(currDepth + 1, maxDepth) }, - { randomNonDet(currDepth + 1, maxDepth) }, - { randomIf(currDepth + 1, maxDepth) }, -// { randomLoop(currDepth + 1, maxDepth) }, - ).random(random)() - } - - fun randomLeaf(): Stmt { - return listOf( -// { Stmts.Skip() }, - { randomAssign() }, - { randomAssign() }, - { randomAssume() }, -// { randomHavoc() }, - ).random(random)() - } - - fun randomSeq(currDepth: Int, maxDepth: Int): Stmt { - return Stmts.SequenceStmt(listOf( - randomIntermediate(currDepth + 1, maxDepth), - randomIntermediate(currDepth + 1, maxDepth) - )) - } - - fun randomNonDet(currDepth: Int, maxDepth: Int): Stmt { - return Stmts.NonDetStmt(listOf( - randomIntermediate(currDepth + 1, maxDepth), - randomIntermediate(currDepth + 1, maxDepth) - )) - - } - - fun randomIf(currDepth: Int, maxDepth: Int): Stmt { - var expr: Expr - do expr = randomBoolExpr(0) - while (ExprUtils.getVars(expr).isEmpty()) - return IfStmt.of(expr, - randomIntermediate(currDepth + 1, maxDepth), - randomIntermediate(currDepth + 1, maxDepth) + lateinit var ctrlVars: List> + lateinit var otherVars: List> + lateinit var boolVars: List> + lateinit var clockVars: List> + lateinit var intVars: List> + + val random = Random(seed) + + fun generateRandomXsts( + depth: Int, + numCtrl: Int, + numClock: Int, + numOther: Int, + constraint: (XSTS) -> Boolean, + ): XSTS { + var xsts: XSTS + do { + val trans = generateRandomStmt(depth, numCtrl, numClock, numOther) + val env = Stmts.NonDetStmt(listOf(Stmts.Skip())) + val init = Stmts.NonDetStmt(listOf(Stmts.Skip())) + val initExpr = + BoolExprs.And( + ctrlVars.map { IntExprs.Eq(it.ref, IntExprs.Int(0)) } + + boolVars.map { BoolExprs.Not(it.ref) } + + intVars.map { IntExprs.Eq(it.ref, IntExprs.Int(0)) } ) - } - - fun randomBoolExpr(currDepth: Int): Expr { - return (if (currDepth == exprMaxDepth) - listOf( - { BoolExprs.True() }, - { boolVars[random.nextInt((boolVars.size))].ref }, - ).random(random) - else - listOf( - { BoolExprs.True() }, - { boolVars[random.nextInt((boolVars.size))].ref }, - { BoolExprs.Not(randomBoolExpr(currDepth + 1)) }, - { - BoolExprs.And( - randomBoolExpr(currDepth + 1), - randomBoolExpr(currDepth + 1) - ) - }, - { - BoolExprs.Or( - randomBoolExpr(currDepth + 1), - randomBoolExpr(currDepth + 1)) - }, - { BoolExprs.Imply(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, - { BoolExprs.Iff(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, - { BoolExprs.Xor(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, - { IntExprs.Eq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Geq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Leq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Lt(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Gt(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Neq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - ).random(random))() - } - - - fun randomIntExpr(currDepth: Int): Expr { - return (if (currDepth == exprMaxDepth) - listOf( - { IntExprs.Int(random.nextInt(5)) }, - { intVars[random.nextInt((intVars.size))].ref }, - ).random(random) - else - listOf( - { IntExprs.Int(random.nextInt(5)) }, - { intVars[random.nextInt((intVars.size))].ref }, - { IntExprs.Neg(randomIntExpr(currDepth + 1)) }, - { IntExprs.Add(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Mul(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - { IntExprs.Sub(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, - ).random(random))() - } - - fun randomLoop(currDepth: Int, maxDepth: Int): Stmt { - return LoopStmt.of(randomIntermediate(currDepth + 1, maxDepth), ctrlVars.random(random), - IntExprs.Int(0), - randomIntExpr(0) { ctrlVars.containsAll(ExprUtils.getVars(it)) } + var expr: Expr + do expr = randomBoolExpr(0) while (ExprUtils.getVars(expr).isEmpty()) + xsts = XSTS(ctrlVars.toSet(), init, NonDetStmt.of(listOf(trans)), env, initExpr, expr) + } while (!(constraint(xsts))) + return xsts + } + + fun generateRandomStmt(depth: Int, numCtrl: Int, numClock: Int, numOther: Int): Stmt { + var i = 2 + ctrlVars = Array(numCtrl) { Decls.Var("ctlr${i++}", IntType.getInstance()) }.toList() + do { + otherVars = + listOf( + Decls.Var("var0", IntType.getInstance()) as VarDecl, + Decls.Var("var1", BoolType.getInstance()) as VarDecl, + ) + Array(numOther - 2) { randomVar("var${i++}") }.toList() + } while (otherVars.all { it.type is IntType } || otherVars.all { it.type is BoolType }) + boolVars = otherVars.filter { it.type is BoolType }.map { it as VarDecl } + intVars = otherVars.filter { it.type is IntType }.map { it as VarDecl } + clockVars = Array(numClock) { Decls.Var("clock${i++}", IntType.getInstance()) }.toList() + + return randomIntermediate(0, depth) + } + + fun randomVar(name: String): VarDecl { + return listOf( + { Decls.Var(name, IntType.getInstance()) as VarDecl }, + { Decls.Var(name, BoolType.getInstance()) as VarDecl }, + ) + .random(random)() + } + + fun randomIntermediate(currDepth: Int, maxDepth: Int): Stmt { + if (currDepth == maxDepth) return randomLeaf() + return listOf( + { randomLeaf() }, + { randomSeq(currDepth + 1, maxDepth) }, + { randomNonDet(currDepth + 1, maxDepth) }, + { randomIf(currDepth + 1, maxDepth) }, + // { randomLoop(currDepth + 1, maxDepth) }, + ) + .random(random)() + } + + fun randomLeaf(): Stmt { + return listOf( + // { Stmts.Skip() }, + { randomAssign() }, + { randomAssign() }, + { randomAssume() }, + // { randomHavoc() }, + ) + .random(random)() + } + + fun randomSeq(currDepth: Int, maxDepth: Int): Stmt { + return Stmts.SequenceStmt( + listOf( + randomIntermediate(currDepth + 1, maxDepth), + randomIntermediate(currDepth + 1, maxDepth), + ) + ) + } + + fun randomNonDet(currDepth: Int, maxDepth: Int): Stmt { + return Stmts.NonDetStmt( + listOf( + randomIntermediate(currDepth + 1, maxDepth), + randomIntermediate(currDepth + 1, maxDepth), + ) + ) + } + + fun randomIf(currDepth: Int, maxDepth: Int): Stmt { + var expr: Expr + do expr = randomBoolExpr(0) while (ExprUtils.getVars(expr).isEmpty()) + return IfStmt.of( + expr, + randomIntermediate(currDepth + 1, maxDepth), + randomIntermediate(currDepth + 1, maxDepth), + ) + } + + fun randomBoolExpr(currDepth: Int): Expr { + return (if (currDepth == exprMaxDepth) + listOf({ BoolExprs.True() }, { boolVars[random.nextInt((boolVars.size))].ref }).random(random) + else + listOf( + { BoolExprs.True() }, + { boolVars[random.nextInt((boolVars.size))].ref }, + { BoolExprs.Not(randomBoolExpr(currDepth + 1)) }, + { BoolExprs.And(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, + { BoolExprs.Or(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, + { BoolExprs.Imply(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, + { BoolExprs.Iff(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, + { BoolExprs.Xor(randomBoolExpr(currDepth + 1), randomBoolExpr(currDepth + 1)) }, + { IntExprs.Eq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Geq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Leq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Lt(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Gt(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Neq(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, ) - } - - fun randomAssign(): Stmt { - return (listOf( - { - val v = otherVars.random(random) - when (v.type) { - is IntType -> Stmts.Assign(v as VarDecl, - randomIntExpr { ExprUtils.getVars(it).size > 0 && it != v.ref }) - - is BoolType -> Stmts.Assign(v as VarDecl, - randomBoolExpr { ExprUtils.getVars(it).size > 0 && it != v.ref }) - - else -> throw Exception() - } - }, - { Stmts.Assign(ctrlVars.random(random), randomIntExpr(0)) }, - ) + (if (clockVars.isEmpty()) listOf() else listOf({ randomClockReset() }))).random(random)() - } - - fun randomIntExpr(currDepth: Int = 0, constraint: (Expr) -> Boolean): Expr { - var res: Expr - do { - res = randomIntExpr(currDepth) - } while (!(constraint(res))) - return res - } - - fun randomBoolExpr(currDepth: Int = 0, constraint: (Expr) -> Boolean): Expr { - var res: Expr - do { - res = randomBoolExpr(currDepth) - } while (!(constraint(res))) - return res - } - - fun randomAssume() = (listOf(this::randomDataAssume) + (if (clockVars.isEmpty()) listOf() else listOf( - this::randomClockConstraint))).random(random)() - - fun randomDataAssume(): Stmt { - var expr: Expr - do expr = randomBoolExpr(0) - while (ExprUtils.getVars(expr).isEmpty()) - - return Stmts.Assume(expr) - } - - fun randomClockReset(): Stmt { - val c = clockVars.random(random) - val resetTo = IntExprs.Int(random.nextInt(4)) - return Stmts.Assign(c, resetTo) - } - - fun randomClockConstraint(): Stmt { - val c = if (clockVars.size == 1) clockVars[0].ref else listOf( + .random(random))() + } + + fun randomIntExpr(currDepth: Int): Expr { + return (if (currDepth == exprMaxDepth) + listOf({ IntExprs.Int(random.nextInt(5)) }, { intVars[random.nextInt((intVars.size))].ref }) + .random(random) + else + listOf( + { IntExprs.Int(random.nextInt(5)) }, + { intVars[random.nextInt((intVars.size))].ref }, + { IntExprs.Neg(randomIntExpr(currDepth + 1)) }, + { IntExprs.Add(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Mul(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + { IntExprs.Sub(randomIntExpr(currDepth + 1), randomIntExpr(currDepth + 1)) }, + ) + .random(random))() + } + + fun randomLoop(currDepth: Int, maxDepth: Int): Stmt { + return LoopStmt.of( + randomIntermediate(currDepth + 1, maxDepth), + ctrlVars.random(random), + IntExprs.Int(0), + randomIntExpr(0) { ctrlVars.containsAll(ExprUtils.getVars(it)) }, + ) + } + + fun randomAssign(): Stmt { + return (listOf( + { + val v = otherVars.random(random) + when (v.type) { + is IntType -> + Stmts.Assign( + v as VarDecl, + randomIntExpr { ExprUtils.getVars(it).size > 0 && it != v.ref }, + ) + + is BoolType -> + Stmts.Assign( + v as VarDecl, + randomBoolExpr { ExprUtils.getVars(it).size > 0 && it != v.ref }, + ) + + else -> throw Exception() + } + }, + { Stmts.Assign(ctrlVars.random(random), randomIntExpr(0)) }, + ) + (if (clockVars.isEmpty()) listOf() else listOf({ randomClockReset() }))) + .random(random)() + } + + fun randomIntExpr(currDepth: Int = 0, constraint: (Expr) -> Boolean): Expr { + var res: Expr + do { + res = randomIntExpr(currDepth) + } while (!(constraint(res))) + return res + } + + fun randomBoolExpr(currDepth: Int = 0, constraint: (Expr) -> Boolean): Expr { + var res: Expr + do { + res = randomBoolExpr(currDepth) + } while (!(constraint(res))) + return res + } + + fun randomAssume() = + (listOf(this::randomDataAssume) + + (if (clockVars.isEmpty()) listOf() else listOf(this::randomClockConstraint))) + .random(random)() + + fun randomDataAssume(): Stmt { + var expr: Expr + do expr = randomBoolExpr(0) while (ExprUtils.getVars(expr).isEmpty()) + + return Stmts.Assume(expr) + } + + fun randomClockReset(): Stmt { + val c = clockVars.random(random) + val resetTo = IntExprs.Int(random.nextInt(4)) + return Stmts.Assign(c, resetTo) + } + + fun randomClockConstraint(): Stmt { + val c = + if (clockVars.size == 1) clockVars[0].ref + else + listOf( { clockVars.random(random).ref }, { - val c1 = clockVars.random(random) - val c2 = (clockVars - listOf(c1)).random() - IntExprs.Sub(c1.ref, c2.ref) - } - ).random()() - - val compareTo = IntExprs.Int(random.nextInt(10)) - return Stmts.Assume(listOf( - { IntExprs.Leq(c, compareTo) }, - { IntExprs.Lt(c, compareTo) }, - { IntExprs.Geq(c, compareTo) }, - { IntExprs.Gt(c, compareTo) }, - { IntExprs.Eq(c, compareTo) }, - ).random(random)()) - } + val c1 = clockVars.random(random) + val c2 = (clockVars - listOf(c1)).random() + IntExprs.Sub(c1.ref, c2.ref) + }, + ) + .random()() + + val compareTo = IntExprs.Int(random.nextInt(10)) + return Stmts.Assume( + listOf( + { IntExprs.Leq(c, compareTo) }, + { IntExprs.Lt(c, compareTo) }, + { IntExprs.Geq(c, compareTo) }, + { IntExprs.Gt(c, compareTo) }, + { IntExprs.Eq(c, compareTo) }, + ) + .random(random)() + ) + } - fun randomHavoc(): Stmt { - return Stmts.Havoc((otherVars + clockVars).random(random)) - } -} \ No newline at end of file + fun randomHavoc(): Stmt { + return Stmts.Havoc((otherVars + clockVars).random(random)) + } +} diff --git a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/XstsSerializer.kt b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/XstsSerializer.kt index 833a03799a..0cb2ccccd3 100644 --- a/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/XstsSerializer.kt +++ b/subprojects/xsts/xsts-analysis/src/main/kotlin/hu/bme/mit/theta/xsts/analysis/util/XstsSerializer.kt @@ -1,4 +1,4 @@ -package hu.bme.mit.theta.xsts.analysis.util/* +/* * Copyright 2024 Budapest University of Technology and Economics * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +13,22 @@ package hu.bme.mit.theta.xsts.analysis.util/* * See the License for the specific language governing permissions and * limitations under the License. */ +package hu.bme.mit.theta.xsts.analysis.util /* + * Copyright 2024 Budapest University of Technology and Economics + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import hu.bme.mit.theta.core.dsl.impl.ExprWriter import hu.bme.mit.theta.core.stmt.* import hu.bme.mit.theta.core.type.Expr @@ -23,85 +39,90 @@ import hu.bme.mit.theta.xsts.XSTS object XstsSerializer : StmtVisitor { - val typeNames = mapOf( - BoolType.getInstance() to "boolean", - IntType.getInstance() to "integer" - ).withDefault { it.toString() } - - fun serializeXsts(xsts: XSTS): String { - val builder = StringBuilder() - for (ctrlVar in xsts.ctrlVars) { - builder.appendLine("ctrl var ${ctrlVar.name} : integer") - } - for (v in xsts.vars - xsts.ctrlVars) { - builder.appendLine("var ${v.name} : ${typeNames[v.type]}") - } - builder.appendLine() - - builder.appendLine("init {\n assume (${serializeExpr(xsts.initFormula)});\n${xsts.init.accept(this, null)}\n}") - builder.appendLine("trans {\n${xsts.tran.accept(this, null)}\n}") - builder.appendLine("env {\n${xsts.env.accept(this, null)}\n}") - - return builder.toString() - } - - fun serializeExpr(expr: Expr<*>): String { - return ExprWriter.instance().write(expr) + val typeNames = + mapOf(BoolType.getInstance() to "boolean", IntType.getInstance() to "integer").withDefault { + it.toString() } - override fun visit(stmt: SkipStmt?, param: Void?): String { - return "" + fun serializeXsts(xsts: XSTS): String { + val builder = StringBuilder() + for (ctrlVar in xsts.ctrlVars) { + builder.appendLine("ctrl var ${ctrlVar.name} : integer") } - - override fun visit(stmt: AssumeStmt, param: Void?): String { - return "assume (${serializeExpr(stmt.cond)});" - } - - override fun visit(stmt: AssignStmt, param: Void?): String { - return "${stmt.varDecl.name} := (${serializeExpr(stmt.expr)});" + for (v in xsts.vars - xsts.ctrlVars) { + builder.appendLine("var ${v.name} : ${typeNames[v.type]}") } - - override fun visit(stmt: HavocStmt, param: Void?): String { - return "havoc ${stmt.varDecl.name};" - } - - override fun visit(stmt: SequenceStmt, param: Void?): String { - return stmt.stmts.joinToString("\n") { "${it.accept(this, null)}" } - } - - override fun visit(stmt: NonDetStmt, param: Void?): String { - return "choice ${stmt.stmts.joinToString(" or ") { "{\n${it.accept(this, null)}\n}" }}" - } - - override fun visit(stmt: OrtStmt?, param: Void?): String { - TODO("Not yet implemented") - } - - override fun visit(stmt: LoopStmt, param: Void?): String { - /* - for i from 1 to y+1 do { - x:=x-1; - } - */ - return """ + builder.appendLine() + + builder.appendLine( + "init {\n assume (${serializeExpr(xsts.initFormula)});\n${xsts.init.accept(this, null)}\n}" + ) + builder.appendLine("trans {\n${xsts.tran.accept(this, null)}\n}") + builder.appendLine("env {\n${xsts.env.accept(this, null)}\n}") + + return builder.toString() + } + + fun serializeExpr(expr: Expr<*>): String { + return ExprWriter.instance().write(expr) + } + + override fun visit(stmt: SkipStmt?, param: Void?): String { + return "" + } + + override fun visit(stmt: AssumeStmt, param: Void?): String { + return "assume (${serializeExpr(stmt.cond)});" + } + + override fun visit(stmt: AssignStmt, param: Void?): String { + return "${stmt.varDecl.name} := (${serializeExpr(stmt.expr)});" + } + + override fun visit(stmt: HavocStmt, param: Void?): String { + return "havoc ${stmt.varDecl.name};" + } + + override fun visit(stmt: SequenceStmt, param: Void?): String { + return stmt.stmts.joinToString("\n") { "${it.accept(this, null)}" } + } + + override fun visit(stmt: NonDetStmt, param: Void?): String { + return "choice ${stmt.stmts.joinToString(" or ") { "{\n${it.accept(this, null)}\n}" }}" + } + + override fun visit(stmt: OrtStmt?, param: Void?): String { + TODO("Not yet implemented") + } + + override fun visit(stmt: LoopStmt, param: Void?): String { + /* + for i from 1 to y+1 do { + x:=x-1; + } + */ + return """ for ${stmt.loopVariable.name} from (${serializeExpr(stmt.from)}) to (${serializeExpr(stmt.to)}) do { ${stmt.stmt.accept(this, null)} - }""".trimIndent() - } + }""" + .trimIndent() + } - override fun visit(stmt: IfStmt, param: Void?): String { - return """ + override fun visit(stmt: IfStmt, param: Void?): String { + return """ if (${serializeExpr(stmt.cond)}) { ${stmt.then.accept(this, null)} } else { ${stmt.elze.accept(this, null)} } - """.trimIndent() - } - - override fun visit( - stmt: MemoryAssignStmt?, param: Void?): String { - TODO("Not yet implemented") - } - + """ + .trimIndent() + } + + override fun visit( + stmt: MemoryAssignStmt?, + param: Void?, + ): String { + TODO("Not yet implemented") + } } diff --git a/subprojects/xsts/xsts/src/main/kotlin/hu/bme/mit/theta/xsts/utils/XSTSVarChanger.kt b/subprojects/xsts/xsts/src/main/kotlin/hu/bme/mit/theta/xsts/utils/XSTSVarChanger.kt index c38b044ec5..c1023766e3 100644 --- a/subprojects/xsts/xsts/src/main/kotlin/hu/bme/mit/theta/xsts/utils/XSTSVarChanger.kt +++ b/subprojects/xsts/xsts/src/main/kotlin/hu/bme/mit/theta/xsts/utils/XSTSVarChanger.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package hu.bme.mit.theta.xsts.utils import hu.bme.mit.theta.core.decl.VarDecl @@ -22,10 +21,17 @@ import hu.bme.mit.theta.core.utils.changeVars import hu.bme.mit.theta.xsts.XSTS fun XSTS.copyWithReplacingVars(variableMapping: Map>): XSTS { - val matchingCtrlVarNames = ctrlVars.filter { variableMapping.containsKey(it.name) }.map { it.name } - val newCtrlVars = ctrlVars.filter { it.name !in variableMapping } - .toSet() + variableMapping.filter { it.key in matchingCtrlVarNames }.values.toSet() - return XSTS(newCtrlVars, init.changeVars(variableMapping) as NonDetStmt, - tran.changeVars(variableMapping) as NonDetStmt, env.changeVars(variableMapping) as NonDetStmt, - initFormula.changeVars(variableMapping), prop.changeVars(variableMapping)) -} \ No newline at end of file + val matchingCtrlVarNames = + ctrlVars.filter { variableMapping.containsKey(it.name) }.map { it.name } + val newCtrlVars = + ctrlVars.filter { it.name !in variableMapping }.toSet() + + variableMapping.filter { it.key in matchingCtrlVarNames }.values.toSet() + return XSTS( + newCtrlVars, + init.changeVars(variableMapping) as NonDetStmt, + tran.changeVars(variableMapping) as NonDetStmt, + env.changeVars(variableMapping) as NonDetStmt, + initFormula.changeVars(variableMapping), + prop.changeVars(variableMapping), + ) +}