diff --git a/build.gradle.kts b/build.gradle.kts index 19d6102585..d097203cde 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,7 @@ buildscript { allprojects { group = "hu.bme.mit.theta" - version = "6.8.3" + version = "6.8.4" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } 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 cd6b33999f..7cbe0ca397 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 @@ -25,6 +25,7 @@ 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.unit.UnitPrec +import hu.bme.mit.theta.common.exception.NotSolvableException import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.core.decl.ConstDecl import hu.bme.mit.theta.core.decl.Decls @@ -50,21 +51,25 @@ import hu.bme.mit.theta.solver.SolverStatus import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.analysis.XcfaPrec import hu.bme.mit.theta.xcfa.model.* +import hu.bme.mit.theta.xcfa.passes.* import kotlin.time.measureTime private val Expr<*>.vars get() = ExprUtils.getVars(this) class XcfaOcChecker( - private val xcfa: XCFA, + xcfa: XCFA, decisionProcedure: OcDecisionProcedureType, private val logger: Logger, conflictInput: String?, private val outputConflictClauses: Boolean, nonPermissiveValidation: Boolean, private val autoConflictConfig: AutoConflictFinderConfig, + private val acceptUnreliableSafe: Boolean = false, ) : SafetyChecker> { + private val xcfa = xcfa.optimizeFurther(OcExtraPasses()) + private var indexing = VarIndexingFactory.indexing(0) private val localVars = mutableMapOf, MutableMap>>() private val memoryDecl = Decls.Var("__oc_checker_memory_declaration__", Int()) @@ -147,7 +152,16 @@ class XcfaOcChecker( else -> SafetyResult.unknown() } } - .also { logger.writeln(Logger.Level.MAINSTEP, "OC checker result: $it") } + .also { + logger.writeln(Logger.Level.MAINSTEP, "OC checker result: $it") + if (xcfa.unsafeUnrollUsed && !acceptUnreliableSafe) { + logger.writeln( + Logger.Level.MAINSTEP, + "Incomplete loop unroll used: safe result is unreliable.", + ) + throw NotSolvableException() + } + } private inner class ThreadProcessor(private val thread: Thread) { 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 bef5ea5729..3e88eb043a 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 @@ -42,6 +42,7 @@ fun getOcChecker( ocConfig.outputConflictClauses, ocConfig.nonPermissiveValidation, ocConfig.autoConflict, + config.outputConfig.acceptUnreliableSafe ) return SafetyChecker { ocChecker.check() } } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex25.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex25.kt index 7af6f13ee0..fb13f97b99 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex25.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/portfolio/complex25.kt @@ -124,20 +124,9 @@ fun complexPortfolio25( debugConfig = portfolioConfig.debugConfig, ) - val forceUnrolledXcfa = - xcfa.optimizeFurther( - listOf( - AssumeFalseRemovalPass(), - MutexToVarPass(), - AtomicReadsOneWritePass(), - LoopUnrollPass(2), - ) - ) - val ocConfig = { inProcess: Boolean -> XcfaConfig( - inputConfig = - baseConfig.inputConfig.copy(xcfaWCtx = Triple(forceUnrolledXcfa, mcm, parseContext)), + inputConfig = baseConfig.inputConfig.copy(xcfaWCtx = Triple(xcfa, mcm, parseContext)), frontendConfig = baseConfig.frontendConfig, backendConfig = BackendConfig( diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaAdapter.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaAdapter.kt index 39d28e6f41..8c0d48b54b 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaAdapter.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/gson/XcfaAdapter.kt @@ -98,7 +98,7 @@ class XcfaAdapter(val gsonSupplier: () -> Gson) : TypeAdapter() { lateinit var vars: Set lateinit var xcfaProcedures: Map lateinit var initProcedures: List>>> - var unsafeUnrollUsed: Boolean = false + var unsafeUnrollUsed = false val varsType = object : TypeToken>() {}.type diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Builders.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Builders.kt index d4e9fbfdc3..4cce1aa289 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Builders.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Builders.kt @@ -34,7 +34,6 @@ constructor( private val procedures: MutableSet = LinkedHashSet(), private val initProcedures: MutableList>>> = ArrayList(), val metaData: MutableMap = LinkedHashMap(), - var unsafeUnrollUsed: Boolean = false, ) { fun getVars(): Set = vars @@ -49,7 +48,6 @@ constructor( globalVars = vars, procedureBuilders = procedures, initProcedureBuilders = initProcedures, - unsafeUnrollUsed = unsafeUnrollUsed, ) } @@ -80,6 +78,7 @@ constructor( private val locs: MutableSet = LinkedHashSet(), private val edges: MutableSet = LinkedHashSet(), val metaData: MutableMap = LinkedHashMap(), + unsafeUnrollUsed: Boolean = false, ) { lateinit var initLoc: XcfaLocation @@ -91,6 +90,9 @@ constructor( var errorLoc: Optional = Optional.empty() private set + var unsafeUnrollUsed: Boolean = unsafeUnrollUsed + private set + lateinit var parent: XcfaBuilder private lateinit var built: XcfaProcedure private lateinit var optimized: XcfaProcedureBuilder @@ -322,6 +324,6 @@ constructor( } fun setUnsafeUnroll() { - parent.unsafeUnrollUsed = true + unsafeUnrollUsed = true } } diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XCFA.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XCFA.kt index f9500c5ddf..b23e1ee315 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XCFA.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XCFA.kt @@ -26,12 +26,12 @@ class XCFA( val globalVars: Set, // global variables val procedureBuilders: Set = emptySet(), val initProcedureBuilders: List>>> = emptyList(), - val unsafeUnrollUsed: Boolean = false, + var unsafeUnrollUsed: Boolean = false ) { - val pointsToGraph by this.lazyPointsToGraph + private var cachedHash: Int? = null - var cachedHash: Int? = null + val pointsToGraph by this.lazyPointsToGraph var procedures: Set // procedure definitions private set @@ -51,6 +51,7 @@ class XCFA( procedures = procedureBuilders.map { it.build(this) }.toSet() initProcedures = initProcedureBuilders.map { Pair(it.first.build(this), it.second) } + unsafeUnrollUsed = (procedureBuilders + initProcedureBuilders.map { it.first }).any { it.unsafeUnrollUsed } } /** Recreate an existing XCFA by substituting the procedures and initProcedures fields. */ diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaFurtherOptimizer.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaFurtherOptimizer.kt index 09000c6e2b..77652f6a59 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaFurtherOptimizer.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaFurtherOptimizer.kt @@ -15,22 +15,20 @@ */ package hu.bme.mit.theta.xcfa.model -import hu.bme.mit.theta.xcfa.passes.ProcedurePass import hu.bme.mit.theta.xcfa.passes.ProcedurePassManager -fun XCFA.optimizeFurther(passes: List): XCFA { - if (passes.isEmpty()) return this - val passManager = ProcedurePassManager(passes) - val copy: XcfaProcedureBuilder.() -> XcfaProcedureBuilder = { - val newLocs = getLocs().associateWith { it.copy() } +fun XCFA.optimizeFurther(passManager: ProcedurePassManager): XCFA { + if (passManager.passes.isEmpty()) return this + val deepCopy: XcfaProcedure.() -> XcfaProcedureBuilder = { + val newLocs = locs.associateWith { it.copy() } XcfaProcedureBuilder( name = name, manager = passManager, - params = getParams().toMutableList(), - vars = getVars().toMutableSet(), - locs = getLocs().map { newLocs[it]!! }.toMutableSet(), + params = params.toMutableList(), + vars = vars.toMutableSet(), + locs = locs.map { newLocs[it]!! }.toMutableSet(), edges = - getEdges() + edges .map { val source = newLocs[it.source]!! val target = newLocs[it.target]!! @@ -40,10 +38,11 @@ fun XCFA.optimizeFurther(passes: List): XCFA { edge } .toMutableSet(), - metaData = metaData.toMutableMap(), + metaData = mutableMapOf(), + unsafeUnrollUsed = unsafeUnrollUsed, ) - .also { - it.copyMetaLocs( + .also { proc -> + proc.copyMetaLocs( newLocs[initLoc]!!, finalLoc.map { newLocs[it] }, errorLoc.map { newLocs[it] }, @@ -52,9 +51,9 @@ fun XCFA.optimizeFurther(passes: List): XCFA { } val builder = XcfaBuilder(name, globalVars.toMutableSet()) - procedureBuilders.forEach { builder.addProcedure(it.copy()) } - initProcedureBuilders.forEach { (proc, params) -> - val initProc = builder.getProcedures().find { it.name == proc.name } ?: proc.copy() + procedures.forEach { builder.addProcedure(it.deepCopy()) } + initProcedures.forEach { (proc, params) -> + val initProc = builder.getProcedures().find { it.name == proc.name } ?: proc.deepCopy() builder.addEntryPoint(initProc, params) } return builder.build() 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 f9979b3aae..77743c2767 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 @@ -18,9 +18,9 @@ package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.frontend.ParseContext -open class ProcedurePassManager(vararg passes: List) { +open class ProcedurePassManager(val passes: List>) { - val passes: List> = passes.toList() + constructor(vararg passes: List) : this(passes.toList()) } class CPasses(checkOverflow: Boolean, parseContext: ParseContext, uniqueWarningLogger: Logger) : @@ -99,3 +99,13 @@ class ChcPasses(parseContext: ParseContext, uniqueWarningLogger: Logger) : ) class LitmusPasses : ProcedurePassManager() + +class OcExtraPasses : + ProcedurePassManager( + listOf( + AssumeFalseRemovalPass(), + MutexToVarPass(), + AtomicReadsOneWritePass(), + LoopUnrollPass(2), // force loop unroll for BMC + ) + )