diff --git a/.github/workflows/mac-build-test.yml b/.github/workflows/mac-build-test.yml index 1473830998..4a0f2183b0 100644 --- a/.github/workflows/mac-build-test.yml +++ b/.github/workflows/mac-build-test.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest, macos-13, macos-12] + os: [macos-13, macos-12] runs-on: ${{ matrix.os }} steps: - name: Checkout repository diff --git a/.gitignore b/.gitignore index 490f179376..6a151e2b52 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ arg-true.dot trace.dot xcfa.c xcfa.dot -xcfa.json \ No newline at end of file +xcfa.json +*.plantuml diff --git a/build.gradle.kts b/build.gradle.kts index 479f4b71a0..12a14fcd19 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ buildscript { allprojects { group = "hu.bme.mit.theta" - version = "5.1.0" + version = "5.1.1" apply(from = rootDir.resolve("gradle/shared-with-buildSrc/mirrors.gradle.kts")) } diff --git a/subprojects/common/analysis/build.gradle.kts b/subprojects/common/analysis/build.gradle.kts index 221ae500cf..6b3894f0be 100644 --- a/subprojects/common/analysis/build.gradle.kts +++ b/subprojects/common/analysis/build.gradle.kts @@ -22,7 +22,11 @@ dependencies { implementation(project(":theta-common")) implementation(project(":theta-core")) implementation(project(":theta-solver")) + implementation(Deps.javasmt) + implementation(project(":theta-solver-javasmt")) + implementation(project(":theta-solver-z3-legacy")) implementation(project(":theta-graph-solver")) + implementation(project(mapOf("path" to ":theta-solver-z3-legacy"))) testImplementation(project(":theta-solver-z3-legacy")) implementation("com.corundumstudio.socketio:netty-socketio:2.0.6") } 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 new file mode 100644 index 0000000000..f57d462220 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/BasicOcChecker.kt @@ -0,0 +1,107 @@ +/* + * 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.booltype.BoolExprs +import hu.bme.mit.theta.solver.Solver +import hu.bme.mit.theta.solver.SolverManager +import hu.bme.mit.theta.solver.SolverStatus +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 + } + + // 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 + } + } + + 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 + } + } + + relations = decisionStack.peek().rels + return solver.status // no conflict found, counterexample is valid + } + + 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 + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..421b55f3c5 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcChecker.kt @@ -0,0 +1,198 @@ +/* + * 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.BoolType +import hu.bme.mit.theta.core.type.booltype.TrueExpr +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] } } + +/** + * 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. + */ +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 +} + +/** + * 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() + + override fun getPropagatedClauses() = propagated.toList() + + 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 + + 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) + } + + 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) + ) + } + + 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 + } +} + +/** + * 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 + } +} + +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() { + + override fun toExprs(): List> = listOf(rf.declRef, w.guardExpr) + wRfRelation.toExprs() +} + +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. + */ +class OcAssignment internal constructor( + val relation: Relation? = null, + val event: E? = null, + val rels: Array>, + val solverLevel: Int = 0, +) { + + 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) + + override fun toString(): String { + return "OcAssignment(relation=$relation, event=$event, solverLevel=$solverLevel)" + } +} 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 new file mode 100644 index 0000000000..7b4e72b6f6 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/OcTypes.kt @@ -0,0 +1,87 @@ +/* + * 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.ConstDecl +import hu.bme.mit.theta.core.decl.Decl +import hu.bme.mit.theta.core.decl.Decls +import hu.bme.mit.theta.core.decl.IndexedConstDecl +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.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 + +/** + * 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 } +abstract class Event( + 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 + } + + 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, +) { + + 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 + + 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 + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..b420177ac1 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/PreventivePropagatorOcChecker.kt @@ -0,0 +1,86 @@ +/* + * 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 new file mode 100644 index 0000000000..06a25a6ae5 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/algorithm/oc/UserPropagatorOcChecker.kt @@ -0,0 +1,125 @@ +/* + * 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.BoolType +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.* + +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() + } + + 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 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) + } + } + + 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) + } + } + } + + override fun propagate(reason: Reason?): Boolean { + reason ?: return false + propagated.add(reason) + userPropagator.propagateConflict(reason.exprs) + return true + } + + protected open fun pop(levels: Int) { + solverLevel -= levels + while (partialAssignment.isNotEmpty() && partialAssignment.peek().solverLevel > solverLevel) { + partialAssignment.pop() + } + } +} \ No newline at end of file diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/ExplState.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/ExplState.java index dc88533301..cfc2821bc0 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/ExplState.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/ExplState.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Map; import java.util.Optional; @@ -128,7 +129,7 @@ public boolean isBottom() { @Override public String toString() { return Utils.lispStringBuilder(ExplState.class.getSimpleName()).aligned() - .addAll(val.getDecls().stream() + .addAll(val.getDecls().stream().sorted(Comparator.comparing((Decl decl) -> decl.getName())) .map(d -> String.format("(%s %s)", d.getName(), eval(d).get()))) .toString(); } diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/StmtApplier.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/StmtApplier.java index c1456be1cf..67945a67c5 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/StmtApplier.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expl/StmtApplier.java @@ -24,6 +24,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -59,6 +60,8 @@ public static ApplyResult apply(final Stmt stmt, final MutableValuation val, if (stmt instanceof AssignStmt) { final AssignStmt assignStmt = (AssignStmt) stmt; return applyAssign(assignStmt, val, approximate); + } else if (stmt instanceof MemoryAssignStmt memoryAssignStmt) { + return applyMemAssign(memoryAssignStmt, val, approximate); } else if (stmt instanceof AssumeStmt) { final AssumeStmt assumeStmt = (AssumeStmt) stmt; return applyAssume(assumeStmt, val, approximate); @@ -87,6 +90,24 @@ public static ApplyResult apply(final Stmt stmt, final MutableValuation val, } } + private static ApplyResult applyMemAssign(MemoryAssignStmt stmt, MutableValuation val, boolean approximate) { + final var expr = ExprUtils.simplify(stmt.getDeref(), val); + final var deref = stmt.getDeref(); + final var newOffset = ExprUtils.simplify(deref.getOffset(), val); +// if (expr instanceof LitExpr litExpr && deref.getArray() instanceof RefExpr refExpr && newOffset instanceof LitExpr litOffset) { +// final VarDecl varDecl = (VarDecl) refExpr.getDecl(); +// final IntLitExpr intLitOffset = (IntLitExpr) litOffset; +// val.put(varDecl.getConstDecl(intLitOffset.getValue().intValue()), litExpr); +// return ApplyResult.SUCCESS; +// } else if (approximate && deref.getArray() instanceof RefExpr refExpr && newOffset instanceof LitExpr litOffset) { +// final VarDecl varDecl = (VarDecl) refExpr.getDecl(); +// final IntLitExpr intLitOffset = (IntLitExpr) litOffset; +// val.remove(varDecl.getConstDecl(intLitOffset.getValue().intValue())); +// return ApplyResult.SUCCESS; +// } + return ApplyResult.FAILURE; + } + private static ApplyResult applyAssign(final AssignStmt stmt, final MutableValuation val, final boolean approximate) { final VarDecl varDecl = stmt.getVarDecl(); diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java index 9f9bfb4d64..f18d4b23d2 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/AasporRefiner.java @@ -22,8 +22,7 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.RefinerResult; import hu.bme.mit.theta.analysis.expr.ExprAction; import hu.bme.mit.theta.analysis.expr.ExprState; -import hu.bme.mit.theta.core.decl.Decl; -import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.decl.VarDecl; import java.util.HashSet; import java.util.Map; @@ -36,11 +35,11 @@ public final class AasporRefiner, Set> ignoredVarRegistry; + private final Map, Set> ignoredVarRegistry; private AasporRefiner(final Refiner refiner, final PruneStrategy pruneStrategy, - final Map, Set> ignoredVarRegistry) { + final Map, Set> ignoredVarRegistry) { this.refiner = refiner; this.pruneStrategy = pruneStrategy; this.ignoredVarRegistry = ignoredVarRegistry; @@ -48,7 +47,7 @@ private AasporRefiner(final Refiner refiner, public static AasporRefiner create( final Refiner refiner, final PruneStrategy pruneStrategy, - final Map, Set> ignoredVarRegistry) { + final Map, Set> ignoredVarRegistry) { return new AasporRefiner<>(refiner, pruneStrategy, ignoredVarRegistry); } @@ -58,7 +57,7 @@ public RefinerResult refine(final ARG arg, final P prec) { if (result.isUnsafe() || pruneStrategy != PruneStrategy.LAZY) return result; final P newPrec = result.asSpurious().getRefinedPrec(); - final Set> newlyAddedVars = new HashSet<>(newPrec.getUsedVars()); + final Set> newlyAddedVars = new HashSet<>(newPrec.getUsedVars()); newlyAddedVars.removeAll(prec.getUsedVars()); newlyAddedVars.forEach(newVar -> { diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java index 66bcfd87cd..599195154e 100644 --- a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/expr/refinement/ExprTraceNewtonChecker.java @@ -34,6 +34,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -282,6 +283,11 @@ public Stmt visit(AssignStmt stmt, Void param) return HavocStmt.of(stmt.getVarDecl()); } + @Override + public Stmt visit(MemoryAssignStmt stmt, Void param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public Stmt visit(HavocStmt stmt, Void param) { return HavocStmt.of(stmt.getVarDecl()); @@ -407,6 +413,11 @@ public Collection> visit(AssignStmt return ExprUtils.getVars(stmt.getExpr()); } + @Override + public Collection> visit(MemoryAssignStmt stmt, Void param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public Collection> visit(HavocStmt stmt, Void param) { return Collections.emptySet(); @@ -455,6 +466,11 @@ public Collection> visit(AssignStmt return Collections.singletonList(stmt.getVarDecl()); } + @Override + public Collection> visit(MemoryAssignStmt stmt, Void param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public Collection> visit(HavocStmt stmt, Void param) { return Collections.emptySet(); @@ -503,6 +519,11 @@ public Collection> visit(AssignStmt return Collections.emptySet(); } + @Override + public Collection> visit(MemoryAssignStmt stmt, Void param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public Collection> visit(HavocStmt stmt, Void param) { return Collections.singletonList(stmt.getVarDecl()); 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 new file mode 100644 index 0000000000..c3ec804041 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/ItpRefToPtrPrec.kt @@ -0,0 +1,47 @@ +/* + * 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.ptr + +import com.google.common.base.Preconditions +import hu.bme.mit.theta.analysis.Prec +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. + */ +class ItpRefToPtrPrec

(private val innerRefToPrec: RefutationToPrec) : + 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 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() + } +} 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 new file mode 100644 index 0000000000..015084a889 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAction.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.analysis.ptr + +import com.google.common.base.Preconditions +import hu.bme.mit.theta.analysis.expr.StmtAction +import hu.bme.mit.theta.core.decl.Decls.Var +import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.stmt.Stmt +import hu.bme.mit.theta.core.stmt.Stmts +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 +import hu.bme.mit.theta.core.type.anytype.Dereference +import hu.bme.mit.theta.core.type.booltype.BoolExprs +import hu.bme.mit.theta.core.type.inttype.IntExprs +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 val stmtList: List + + private val expanded by lazy { createStmtList(writeTriples) } + + var cnts = LinkedHashMap() + fun nextWriteTriples(): WriteTriples = expanded.first + + final override fun getStmts(): List = expanded.second + + fun havocStmts(): List = expanded.third + + 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() + + 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 + } + + stmtList.addAll(preList) + stmtList.add(stmt) + havocStmtList.add(stmt) + stmtList.addAll(postList) + } + return Triple(nextWriteTriples, stmtList, havocStmtList) + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..4eb29ec625 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysis.kt @@ -0,0 +1,72 @@ +/* + * 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.ptr + +import hu.bme.mit.theta.analysis.* +import hu.bme.mit.theta.analysis.expr.ExprAction +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.expr.StmtAction +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. + * + * 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

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

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

> = innerAnalysis.transFunc.getPtrTransFunc( + isHavoc) +} + +fun PartialOrd.getPtrPartialOrd(): PartialOrd> = PartialOrd { state1, state2 -> + isLeq(state1.innerState, state2.innerState) +} + +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 + } + } else { + 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/PtrPrec.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrPrec.kt new file mode 100644 index 0000000000..ace61e91c7 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrPrec.kt @@ -0,0 +1,24 @@ +/* + * 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.ptr + +import hu.bme.mit.theta.analysis.Prec +import hu.bme.mit.theta.core.decl.VarDecl + +data class PtrPrec

(val innerPrec: P, val set: Set = emptySet(), val smth: Int = 0) : Prec { + + override fun getUsedVars(): Collection> = innerPrec.usedVars +} \ No newline at end of file diff --git a/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrState.kt b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrState.kt new file mode 100644 index 0000000000..85652e3f1c --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrState.kt @@ -0,0 +1,34 @@ +/* + * 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.ptr + +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.booltype.BoolType + +data class PtrState @JvmOverloads constructor( + val innerState: S, + val nextCnt: Int = 0, +) : ExprState { + + override fun isBottom(): Boolean { + return innerState.isBottom() + } + + override fun toExpr(): Expr { + return innerState.toExpr() + } +} 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 new file mode 100644 index 0000000000..3cc08a0cf1 --- /dev/null +++ b/subprojects/common/analysis/src/main/java/hu/bme/mit/theta/analysis/ptr/PtrUtils.kt @@ -0,0 +1,211 @@ +/* + * 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.ptr + +import com.google.common.base.Preconditions +import hu.bme.mit.theta.analysis.Prec +import hu.bme.mit.theta.analysis.expl.ExplPrec +import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.pred.PredPrec +import hu.bme.mit.theta.analysis.pred.PredState +import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.stmt.* +import hu.bme.mit.theta.core.stmt.Stmts.Assume +import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.LitExpr +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.IteExpr +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.inttype.IntExprs.Int +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>>> + +fun WriteTriples.toMutable(): MutableWriteTriples = + LinkedHashMap(this.map { Pair(it.key, ArrayList(it.value)) }.toMap()) + +enum class AccessType { + 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}") + } + +val Expr<*>.dereferences: List> + get() = if (this is Dereference<*, *, *>) { + listOf(this) + ops.flatMap { it.dereferences } + } else { + 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 + } + +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() +} + +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 fun contains(element: Expr<*>): Boolean = true + + override fun containsAll(elements: Collection>) = true + + override fun isEmpty(): Boolean = false + + 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 +} + +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) { + 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 + else -> error("State $this is not supported") +} + +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) } + else -> map { it.patch(writeTriples) } +} + + +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) { + 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) { + 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 de8546bb25..65ff75d6cf 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 @@ -22,7 +22,6 @@ import hu.bme.mit.theta.analysis.algorithm.ArgTrace import hu.bme.mit.theta.analysis.runtimemonitor.container.CexHashStorage import hu.bme.mit.theta.common.exception.NotSolvableException import hu.bme.mit.theta.common.logging.Logger -import java.lang.RuntimeException /** * This monitor checks whether a new counterexample is found during the current iteration of CEGAR. @@ -41,10 +40,10 @@ class CexMonitor constructor( fun checkIfNewCexFound(): Boolean { return if (arg.cexs.anyMatch { cex -> !cexHashStorage.contains(cex) }) { logger.write(Logger.Level.VERBOSE, - "Counterexample hash check: new cex found successfully") + "Counterexample hash check: new cex found successfully\n") true } else { - logger.write(Logger.Level.INFO, "Counterexample hash check: NO new cex found") + logger.write(Logger.Level.INFO, "Counterexample hash check: NO new cex found\n") false } } 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 new file mode 100644 index 0000000000..9379846676 --- /dev/null +++ b/subprojects/common/analysis/src/test/java/hu/bme/mit/theta/analysis/ptr/PtrAnalysisTest.kt @@ -0,0 +1,81 @@ +/* + * 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.ptr + +import hu.bme.mit.theta.analysis.expl.ExplAnalysis +import hu.bme.mit.theta.analysis.expl.ExplPrec +import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.core.decl.Decls.Var +import hu.bme.mit.theta.core.stmt.Stmt +import hu.bme.mit.theta.core.stmt.Stmts.Assume +import hu.bme.mit.theta.core.stmt.Stmts.MemoryAssign +import hu.bme.mit.theta.core.type.anytype.Exprs.Dereference +import hu.bme.mit.theta.core.type.booltype.BoolExprs.True +import hu.bme.mit.theta.core.type.inttype.IntExprs.Eq +import hu.bme.mit.theta.core.type.inttype.IntExprs.Int +import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory +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 + +class PtrAnalysisTest { + + companion object { + + private val x = Var("x", Int()) + + 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 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))), + ) + } + } + + @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 diff --git a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/LispStringBuilder.java b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/LispStringBuilder.java index 4367b2384f..a0d3f7aa99 100644 --- a/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/LispStringBuilder.java +++ b/subprojects/common/common/src/main/java/hu/bme/mit/theta/common/LispStringBuilder.java @@ -15,15 +15,15 @@ */ package hu.bme.mit.theta.common; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; +import com.google.common.base.Strings; import java.util.ArrayList; import java.util.List; import java.util.StringJoiner; import java.util.stream.Stream; -import com.google.common.base.Strings; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; /** * Utility class for printing Lisp style strings, e.g., (A (B 1 2) (C 3)). @@ -41,6 +41,8 @@ public final class LispStringBuilder { private State state; + private int minLengthForMultiline = 75; + private static enum State { HEAD, ALIGNED, BODY, BUILT; } @@ -108,7 +110,13 @@ public String toString() { sb.append(body); sb.append(RPAREN); - return sb.toString(); + final String ret = sb.toString(); + final String singleLine = ret.replaceAll("\\s+", " "); + if (singleLine.length() < minLengthForMultiline) { + return singleLine; + } else { + return ret; + } } //// diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/clock/op/ClockOps.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/clock/op/ClockOps.java index ca5a72fb45..1f4689baff 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/clock/op/ClockOps.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/clock/op/ClockOps.java @@ -24,6 +24,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -174,6 +175,11 @@ public ClockOp visit(final AssignStmt stmt, throw new IllegalArgumentException(); } + @Override + public ClockOp visit(MemoryAssignStmt stmt, Void param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public ClockOp visit(final AssumeStmt stmt, final Void param) { try { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/Decl.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/Decl.java index 3826b9d3c3..fd3b4ac587 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/Decl.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/Decl.java @@ -33,7 +33,7 @@ public abstract class Decl { public Decl(final String name, final DeclType type) { checkNotNull(name); - checkArgument(name.length() > 0); + checkArgument(!name.isEmpty()); this.name = name; this.type = checkNotNull(type); this.ref = Ref(this); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/IndexedVarDecl.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/IndexedVarDecl.java new file mode 100644 index 0000000000..3db7eac289 --- /dev/null +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/IndexedVarDecl.java @@ -0,0 +1,50 @@ +/* + * 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.core.decl; + +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.RefExpr; + +public class IndexedVarDecl extends VarDecl { + + private final VarDecl original; + private final ConstDecl constDecl; + private final RefExpr constRef; + + IndexedVarDecl(final String name, final VarDecl original) { + super(name, original.getType()); + this.original = original; + this.constDecl = Decls.Const(name, original.getType()); + this.constRef = RefExpr.of(constDecl); + } + + public static IndexedVarDecl of(final String name, final VarDecl original) { + return new IndexedVarDecl<>(name, original); + } + + public VarDecl getOriginal() { + return original; + } + + public RefExpr getConstRef() { + return constRef; + } + + public ConstDecl getConst() { + return constDecl; + } +} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/VarDecl.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/VarDecl.java index 8c942f0eca..416c951247 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/VarDecl.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/decl/VarDecl.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.RefExpr; import java.util.Map; @@ -30,7 +31,7 @@ * * @param */ -public final class VarDecl extends Decl { +public class VarDecl extends Decl { private static final String DECL_LABEL = "var"; diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/dsl/impl/StmtWriter.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/dsl/impl/StmtWriter.java index 8c03963dcc..1d872c615e 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/dsl/impl/StmtWriter.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/dsl/impl/StmtWriter.java @@ -20,6 +20,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -49,6 +50,11 @@ public String visit(final AssignStmt stmt, fin return stmt.getVarDecl().getName() + " := " + writeExpr(stmt.getExpr()); } + @Override + public String visit(MemoryAssignStmt stmt, Void param) { + return stmt.getDeref() + " := " + writeExpr(stmt.getExpr()); + } + @Override public String visit(final HavocStmt stmt, final Void param) { return "havoc " + stmt.getVarDecl().getName(); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/MemoryAssignStmt.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/MemoryAssignStmt.java new file mode 100644 index 0000000000..333148d750 --- /dev/null +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/MemoryAssignStmt.java @@ -0,0 +1,93 @@ +/* + * 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.core.stmt; + +import com.google.common.base.Objects; +import hu.bme.mit.theta.common.Utils; +import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Assignment statement of the form *(DEREF_EXPRESSION) := EXPRESSION. The statement updates the + * value pointed to by DEREF_EXPRESSION with the result of EXPRESSION. + * + * @param + */ +public final class MemoryAssignStmt implements Stmt { + + private static final String STMT_LABEL = "memassign"; + + private final Dereference deref; + private final Expr expr; + + private MemoryAssignStmt(final Dereference deref, final Expr expr) { + this.deref = checkNotNull(deref); + this.expr = checkNotNull(expr); + } + + public static MemoryAssignStmt of( + final Dereference deref, + final Expr expr) { + return new MemoryAssignStmt<>(deref, expr); + } + + @SuppressWarnings("unchecked") + public static MemoryAssignStmt create( + final Dereference deref, + final Expr expr) { + final Dereference typedDeref = (Dereference) deref; + final Expr typedExpr = expr; + return MemoryAssignStmt.of(typedDeref, typedExpr); + } + + public Dereference getDeref() { + return deref; + } + + public Expr getExpr() { + return expr; + } + + @Override + public R accept(final StmtVisitor visitor, final P param) { + return visitor.visit(this, param); + } + + @Override + public int hashCode() { + return Objects.hashCode(deref, expr); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj != null && this.getClass() == obj.getClass()) { + return Objects.equal(deref, ((MemoryAssignStmt) obj).deref) && + Objects.equal(expr, ((MemoryAssignStmt) obj).expr); + } else { + return false; + } + } + + @Override + public String toString() { + return Utils.lispStringBuilder(STMT_LABEL).add(deref).add(expr).toString(); + } +} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/StmtVisitor.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/StmtVisitor.java index 6964f5b67f..81ec3f4deb 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/StmtVisitor.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/StmtVisitor.java @@ -25,6 +25,8 @@ public interface StmtVisitor { R visit(AssignStmt stmt, P param); + R visit(MemoryAssignStmt stmt, P param); + R visit(HavocStmt stmt, P param); R visit(SequenceStmt stmt, P param); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/Stmts.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/Stmts.java index 0a3e661891..1a07a5b69a 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/Stmts.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/stmt/Stmts.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.core.decl.VarDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.booltype.BoolType; import java.util.List; @@ -44,6 +45,10 @@ public static AssignStmt Assign(final VarDecl lhs, final return AssignStmt.of(lhs, rhs); } + public static

MemoryAssignStmt MemoryAssign(final Dereference deref, final Expr expr) { + return MemoryAssignStmt.of(deref, expr); + } + public static HavocStmt Havoc(final VarDecl varDecl) { return HavocStmt.of(varDecl); } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/BinaryExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/BinaryExpr.java index 5effd05265..9de08267f3 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/BinaryExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/BinaryExpr.java @@ -15,16 +15,15 @@ */ package hu.bme.mit.theta.core.type; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.List; - import com.google.common.collect.ImmutableList; - import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.core.utils.TypeUtils; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + public abstract class BinaryExpr implements Expr { @@ -80,7 +79,7 @@ public final int hashCode() { @Override public final String toString() { - return Utils.lispStringBuilder(getOperatorLabel()).add(leftOp).add(rightOp).toString(); + return Utils.lispStringBuilder(getOperatorLabel()).body().add(leftOp).add(rightOp).toString(); } public abstract BinaryExpr with(final Expr leftOp, diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/MultiaryExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/MultiaryExpr.java index d4b1876205..b5c9c56e26 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/MultiaryExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/MultiaryExpr.java @@ -15,16 +15,15 @@ */ package hu.bme.mit.theta.core.type; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.ImmutableList.toImmutableList; - -import java.util.List; - import com.google.common.collect.ImmutableList; - import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.core.utils.TypeUtils; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ImmutableList.toImmutableList; + public abstract class MultiaryExpr implements Expr { @@ -73,7 +72,7 @@ public final int hashCode() { @Override public final String toString() { - return Utils.lispStringBuilder(getOperatorLabel()).addAll(ops).toString(); + return Utils.lispStringBuilder(getOperatorLabel()).body().addAll(ops).toString(); } public abstract MultiaryExpr with(final Iterable> ops); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/UnaryExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/UnaryExpr.java index 0c477b4332..9a82743233 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/UnaryExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/UnaryExpr.java @@ -15,16 +15,15 @@ */ package hu.bme.mit.theta.core.type; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.List; - import com.google.common.collect.ImmutableList; - import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.core.utils.TypeUtils; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + public abstract class UnaryExpr implements Expr { @@ -72,7 +71,7 @@ public final int hashCode() { @Override public final String toString() { - return Utils.lispStringBuilder(getOperatorLabel()).add(op).toString(); + return Utils.lispStringBuilder(getOperatorLabel()).body().add(op).toString(); } public abstract UnaryExpr with(final Expr op); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Dereference.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Dereference.java new file mode 100644 index 0000000000..1622683564 --- /dev/null +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Dereference.java @@ -0,0 +1,134 @@ +/* + * 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.core.type.anytype; + +import hu.bme.mit.theta.common.Utils; +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.Type; +import hu.bme.mit.theta.core.type.inttype.IntType; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkState; + +public final class Dereference implements Expr { + + private static final String OPERATOR_LABEL = "deref"; + private final Expr array; + private final Expr offset; + private final T type; + + private final Optional> uniquenessIdx; + + private Dereference(Expr array, Expr offset, T type) { + this.array = array; + this.offset = offset; + this.type = type; + uniquenessIdx = Optional.empty(); + } + + + private Dereference(Expr array, Expr offset, Expr uniqueness, T type) { + this.array = array; + this.offset = offset; + this.type = type; + this.uniquenessIdx = Optional.ofNullable(uniqueness); + } + + public Expr getArray() { + return array; + } + + public Expr getOffset() { + return offset; + } + + + public static Dereference of(Expr array, Expr offset, T type) { + return new Dereference<>(array, offset, type); + } + + private static Dereference of(Expr array, Expr offset, Expr uniqueness, T type) { + return new Dereference<>(array, offset, uniqueness, type); + } + + public Dereference withUniquenessExpr(Expr expr) { + return Dereference.of(array, offset, expr, type); // TODO: this kills the stuck check + } + + @Override + public int getArity() { + return 3; + } + + @Override + public T getType() { + return type; + } + + @Override + public LitExpr eval(Valuation val) { + throw new IllegalStateException( + "Reference/Dereference expressions are not meant to be evaluated!"); + } + + @Override + public List> getOps() { + return uniquenessIdx.isPresent() ? List.of(array, offset, uniquenessIdx.get()) : List.of(array, offset); + } + + @Override + public Expr withOps(List> ops) { + checkState(ops.size() == 3 || ops.size() == 2); + if (ops.size() == 3) { + return Dereference.of(ops.get(0), ops.get(1), (Expr) ops.get(2), type); + } else { + return Dereference.of(ops.get(0), ops.get(1), type); + } + } + + @Override + public int hashCode() { + return Objects.hash(array, offset, uniquenessIdx, type); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Dereference that) { + return Objects.equals(this.array, that.array) && + Objects.equals(this.offset, that.offset) && + Objects.equals(this.uniquenessIdx, that.uniquenessIdx) && + Objects.equals(this.type, that.type); + } + return false; + } + + @Override + public String toString() { + var base = Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getArray()).add(getOffset()); + uniquenessIdx.ifPresent(base::add); + return base.add(type).toString(); + } + + public Optional> getUniquenessIdx() { + return uniquenessIdx; + } +} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Exprs.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Exprs.java index 8f2bb1bb1a..4a55648279 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Exprs.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Exprs.java @@ -15,13 +15,13 @@ */ package hu.bme.mit.theta.core.type.anytype; -import static com.google.common.base.Preconditions.checkArgument; - import hu.bme.mit.theta.core.decl.Decl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; import hu.bme.mit.theta.core.type.booltype.BoolType; +import static com.google.common.base.Preconditions.checkArgument; + public final class Exprs { private Exprs() { @@ -41,6 +41,16 @@ public static PrimeExpr Prime(final Expr + Dereference Dereference(final Expr arr, final Expr offset, final ExprType type) { + return Dereference.of(arr, offset, type); + } + + public static + Reference Reference(final Expr expr, final ArrType type) { + return Reference.of(expr, type); + } + /* * Convenience methods */ diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/IteExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/IteExpr.java index 8585f29b08..f22316a8e4 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/IteExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/IteExpr.java @@ -160,7 +160,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(getCond()).add(getThen()).add(getElse()) + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getCond()).add(getThen()).add(getElse()) .toString(); } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Reference.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Reference.java new file mode 100644 index 0000000000..578f319c7e --- /dev/null +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/anytype/Reference.java @@ -0,0 +1,97 @@ +/* + * 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.core.type.anytype; + +import hu.bme.mit.theta.common.Utils; +import hu.bme.mit.theta.core.decl.VarDecl; +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.Type; + +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkState; + +public final class Reference implements Expr { + + private static final String OPERATOR_LABEL = "ref"; + private final Expr expr; + private final A type; + + private Reference(Expr expr, A type) { + this.expr = expr; + this.type = type; + } + + public Expr getExpr() { + return expr; + } + + public static Reference of(Expr expr, A type) { + return new Reference<>(expr, type); + } + + @Override + public int getArity() { + return 1; + } + + @Override + public A getType() { + return type; + } + + @Override + public LitExpr eval(Valuation val) { + throw new IllegalStateException( + "Reference/Dereference expressions are not meant to be evaluated!"); + } + + @Override + public List> getOps() { + return List.of(expr); + } + + @Override + public Expr withOps(List> ops) { + checkState(ops.size() == 1); + checkState(ops.get(0) instanceof RefExpr && ((RefExpr) ops.get(0)).getDecl() instanceof VarDecl, "Don't transform references to constants."); + return Reference.of((Expr) ops.get(0), type); + } + + @Override + public int hashCode() { + return Objects.hash(expr, type); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Reference that) { + return Objects.equals(this.expr, that.expr) && + Objects.equals(this.type, that.type); + } + return false; + } + + @Override + public String toString() { + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getExpr()).add(type) + .toString(); + } +} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayLitExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayLitExpr.java index 563d607391..012507dcff 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayLitExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayLitExpr.java @@ -114,7 +114,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL) + return Utils.lispStringBuilder(OPERATOR_LABEL).body() .addAll(elems.stream().map(elem -> String.format("(%s %s)", elem.get1(), elem.get2()))) .add((String.format("(default %s)", elseElem))) .toString(); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayReadExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayReadExpr.java index 482045c425..ed1bfe236b 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayReadExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayReadExpr.java @@ -152,7 +152,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(array).add(index).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(array).add(index).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayType.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayType.java index 13592f38b2..3e6c106a2f 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayType.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayType.java @@ -15,8 +15,6 @@ */ package hu.bme.mit.theta.core.type.arraytype; -import static com.google.common.base.Preconditions.checkNotNull; - import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; @@ -24,6 +22,8 @@ import hu.bme.mit.theta.core.type.abstracttype.Equational; import hu.bme.mit.theta.core.type.abstracttype.NeqExpr; +import static com.google.common.base.Preconditions.checkNotNull; + public final class ArrayType implements Equational> { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayWriteExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayWriteExpr.java index 973f394880..b14ad4ef93 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayWriteExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/arraytype/ArrayWriteExpr.java @@ -170,7 +170,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(array).add(index).add(elem).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(array).add(index).add(elem).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/booltype/QuantifiedExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/booltype/QuantifiedExpr.java index 3a1f90e76d..52c3f0a577 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/booltype/QuantifiedExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/booltype/QuantifiedExpr.java @@ -15,21 +15,20 @@ */ package hu.bme.mit.theta.core.type.booltype; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; -import static java.util.stream.Collectors.joining; - -import java.util.List; - import com.google.common.collect.ImmutableList; - import hu.bme.mit.theta.common.Utils; import hu.bme.mit.theta.core.decl.ParamDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; import hu.bme.mit.theta.core.utils.TypeUtils; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; +import static java.util.stream.Collectors.joining; + public abstract class QuantifiedExpr implements Expr { private final List> paramDecls; @@ -92,7 +91,7 @@ public final String toString() { final String paramString = paramDecls.stream() .map(p -> "(" + p.getName() + " " + p.getType() + ")") .collect(joining(" ", "(", ")")); - return Utils.lispStringBuilder(getOperatorLabel()).add(paramString).add(op).toString(); + return Utils.lispStringBuilder(getOperatorLabel()).body().add(paramString).add(op).toString(); } public abstract QuantifiedExpr with(final Expr op); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvConcatExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvConcatExpr.java index 410b2154b1..cc8e14aa7d 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvConcatExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvConcatExpr.java @@ -108,6 +108,6 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).addAll(getOps()).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().addAll(getOps()).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvExtractExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvExtractExpr.java index 6b4cfb7be5..1afcdc0f1a 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvExtractExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvExtractExpr.java @@ -147,7 +147,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(getBitvec()).add(getFrom()) + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getBitvec()).add(getFrom()) .add(getUntil()).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvSExtExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvSExtExpr.java index 9e328c830b..dec5b61913 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvSExtExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvSExtExpr.java @@ -117,6 +117,6 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(getOp()).add(getType()).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getOp()).add(getType()).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvZExtExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvZExtExpr.java index 26239c9c3d..459ca34716 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvZExtExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/bvtype/BvZExtExpr.java @@ -117,6 +117,6 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder(OPERATOR_LABEL).add(getOp()).add(getType()).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(getOp()).add(getType()).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpToBvExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpToBvExpr.java index 235483fca6..0a22631eb2 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpToBvExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpToBvExpr.java @@ -115,12 +115,12 @@ protected int getHashSeed() { return HASH_SEED; } - public String getOperatorLabel() { - return OPERATOR_LABEL; - } - public FpRoundingMode getRoundingMode() { return roundingMode; } + + public String getOperatorLabel() { + return OPERATOR_LABEL + "[" + size + "'" + (sgn ? "s" : "u") + "][" + roundingMode.name() + "]"; + } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpType.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpType.java index 17e46be03d..9914300c06 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpType.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/fptype/FpType.java @@ -68,7 +68,7 @@ public int getSignificand() { @Override public EqExpr Eq(Expr leftOp, Expr rightOp) { - return FpAssignExpr.of(leftOp, rightOp); + return FpEqExpr.of(leftOp, rightOp); } @Override diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncAppExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncAppExpr.java index f06d19a8ef..2fceca7444 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncAppExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncAppExpr.java @@ -135,7 +135,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return Utils.lispStringBuilder().add(func).add(param).toString(); + return Utils.lispStringBuilder().add(func).body().add(param).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncLitExpr.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncLitExpr.java index 2401d80550..b9837046d4 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncLitExpr.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/type/functype/FuncLitExpr.java @@ -123,7 +123,7 @@ public boolean equals(final Object obj) { @Override public String toString() { final String paramString = String.format("(%s %s)", param.getName(), param.getType()); - return Utils.lispStringBuilder(OPERATOR_LABEL).add(paramString).add(result).toString(); + return Utils.lispStringBuilder(OPERATOR_LABEL).body().add(paramString).add(result).toString(); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprAtomCollector.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprAtomCollector.java index 91b2e60789..a6eca8dd64 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprAtomCollector.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprAtomCollector.java @@ -15,13 +15,9 @@ */ package hu.bme.mit.theta.core.utils; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; - -import java.util.Collection; - import com.google.common.collect.ImmutableSet; - import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.PrimeExpr; import hu.bme.mit.theta.core.type.booltype.AndExpr; import hu.bme.mit.theta.core.type.booltype.BoolType; @@ -30,6 +26,10 @@ import hu.bme.mit.theta.core.type.booltype.NotExpr; import hu.bme.mit.theta.core.type.booltype.OrExpr; +import java.util.Collection; + +import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; + final class ExprAtomCollector { private static final Collection> CONNECTIVES = ImmutableSet.>builder() @@ -45,6 +45,7 @@ final class ExprAtomCollector { .add(OrExpr.class) // .add(IteExpr.class) + .add(Dereference.class) .add(PrimeExpr.class) diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprSimplifier.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprSimplifier.java index d12faaf842..485d923e9c 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprSimplifier.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/ExprSimplifier.java @@ -22,6 +22,7 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; @@ -371,6 +372,12 @@ public static ExprSimplifier create(final SimplifierLevel level) { .addCase(IteExpr.class, this::simplifyIte) + // Reference + + .addCase(Dereference.class, this::simplifyDereference) + +// .addCase(Reference.class, this::simplifyReference) + // Default .addDefault((o, val) -> { @@ -430,6 +437,10 @@ private Expr simplifyGenericIte(final IteExpr< return expr.with(cond, then, elze); } + private Expr simplifyDereference(final Dereference expr, final Valuation val) { + return expr.map(it -> simplify(it, val)); + } + private Expr simplifyArrayRead(final ArrayReadExpr expr, final Valuation val) { return simplifyGenericArrayRead(expr, val); } @@ -1944,6 +1955,7 @@ private Expr simplifyFpRoundToIntegral(final FpRoundToIntegralExpr expr, } private Expr simplifyFpMul(final FpMulExpr expr, final Valuation val) { + if (true) return expr; // Rationale: https://github.com/ftsrg/theta/issues/180 final List> ops = new ArrayList<>(); for (final Expr op : expr.getOps()) { @@ -1996,12 +2008,6 @@ private Expr simplifyFpDiv(final FpDivExpr expr, final Valuation val) { return leftLit.div(expr.getRoundingMode(), rightLit); } - if (leftOp instanceof RefExpr && rightOp instanceof RefExpr) { - if (leftOp.equals(rightOp)) { - return FpUtils.bigFloatToFpLitExpr(new BigFloat(1.0f, FpUtils.getMathContext(expr.getType(), expr.getRoundingMode())), expr.getType()); - } - } - return expr.with(leftOp, rightOp); } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java index 8eee5af268..4df83977c9 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/FpUtils.java @@ -115,8 +115,8 @@ public static FpLitExpr fromString(final String value, final FpType type) { return bigFloatToFpLitExpr(new BigFloat( value, new BinaryMathContext( - type.getExponent(), - type.getSignificand())), + type.getSignificand(), + type.getExponent())), type); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/SpState.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/SpState.java index 8d08cbeb09..f271a0f752 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/SpState.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/SpState.java @@ -25,6 +25,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -147,6 +148,11 @@ public SpState visit(final AssignStmt stmt, return new SpState(expr, constCount); } + @Override + public SpState visit(MemoryAssignStmt stmt, SpState param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public SpState visit(final HavocStmt stmt, final SpState state) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtAtomCollector.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtAtomCollector.java index 6ca8cc1c7a..d064a1549d 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtAtomCollector.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtAtomCollector.java @@ -22,6 +22,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -65,6 +66,13 @@ public Void visit(AssignStmt stmt, return null; } + @Override + public Void visit(MemoryAssignStmt stmt, Set> atoms) { + final Expr eq = EqExpr.create2(stmt.getDeref(), stmt.getExpr()); + atoms.addAll(ExprUtils.getAtoms(eq)); + return null; + } + @Override public Void visit(HavocStmt stmt, Set> atoms) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtCounterVisitor.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtCounterVisitor.java index 716cef5bd8..ce78170227 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtCounterVisitor.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtCounterVisitor.java @@ -21,6 +21,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -57,6 +58,11 @@ public Integer visit(AssignStmt stmt, Void par return 1; } + @Override + public Integer visit(MemoryAssignStmt stmt, Void param) { + return 1; + } + @Override public Integer visit(HavocStmt stmt, Void param) { return 1; diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtSimplifier.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtSimplifier.java index 04ceb96098..5ef35a2789 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtSimplifier.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtSimplifier.java @@ -26,6 +26,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -35,8 +36,11 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; +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.BoolLitExpr; 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.inttype.IntLitExpr; import java.math.BigInteger; @@ -60,7 +64,7 @@ private enum SimplifyStatus { SUCCESS, BOTTOM } - private static class SimplifyResult { + public static class SimplifyResult { private final Stmt stmt; private final SimplifyStatus status; @@ -73,9 +77,17 @@ private SimplifyResult(final Stmt stmt, final SimplifyStatus status) { this.stmt = stmt; this.status = status; } + + public Stmt getStmt() { + return stmt; + } + + public SimplifyStatus getStatus() { + return status; + } } - private static class StmtSimplifierVisitor implements + public static class StmtSimplifierVisitor implements StmtVisitor { @Override @@ -110,6 +122,26 @@ public SimplifyResult visit(final AssignStmt s return SimplifyResult.of(AssignStmt.of(varDecl, expr), SimplifyStatus.SUCCESS); } + @Override + public SimplifyResult visit(MemoryAssignStmt stmt, MutableValuation valuation) { + final Expr expr = ExprUtils.simplify(stmt.getExpr(), valuation); + final Dereference deref = (Dereference) ExprUtils.simplify(stmt.getDeref(), valuation); + + if (expr instanceof LitExpr litExpr && deref.getOffset() instanceof LitExpr litOffset && deref.getArray() instanceof RefExpr ref) { + if (litOffset instanceof IntLitExpr) { + IntLitExpr intLitOffset = (IntLitExpr) litOffset; + VarDecl varDecl = (VarDecl) ref.getDecl(); + valuation.put(varDecl.getConstDecl(intLitOffset.getValue().intValue()), litExpr); + } else if (litOffset instanceof BvLitExpr) { + BvLitExpr bvLitExpr = (BvLitExpr) litOffset; + VarDecl varDecl = (VarDecl) ref.getDecl(); + valuation.put(varDecl.getConstDecl(BvUtils.neutralBvLitExprToBigInteger(bvLitExpr).intValue()), litExpr); + } + } + + return SimplifyResult.of(MemoryAssignStmt.of(deref, expr), SimplifyStatus.SUCCESS); + } + @Override public SimplifyResult visit(final HavocStmt stmt, final MutableValuation valuation) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtToExprTransformer.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtToExprTransformer.java index c8aa3bb255..1345d096f8 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtToExprTransformer.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/StmtToExprTransformer.java @@ -22,6 +22,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -30,6 +31,7 @@ import hu.bme.mit.theta.core.stmt.StmtVisitor; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.booltype.SmartBoolExprs; import hu.bme.mit.theta.core.type.fptype.FpType; @@ -121,6 +123,15 @@ public StmtUnfoldResult visit(final AssignStmt return StmtUnfoldResult.of(ImmutableList.of(expr), newIndexing); } + @Override + public StmtUnfoldResult visit(MemoryAssignStmt stmt, VarIndexing indexing) { + final Expr rhs = ExprUtils.applyPrimes(stmt.getExpr(), indexing); + final Dereference lhs = (Dereference) ExprUtils.applyPrimes(stmt.getDeref(), indexing); + + final var retExpr = Eq(lhs, rhs); + return StmtUnfoldResult.of(ImmutableList.of(retExpr), indexing); + } + @Override public StmtUnfoldResult visit(SequenceStmt sequenceStmt, VarIndexing indexing) { final StmtUnfoldResult result = toExpr(sequenceStmt.getStmts(), indexing); diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/TypeUtils.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/TypeUtils.java index ca7d133c47..942741f860 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/TypeUtils.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/TypeUtils.java @@ -20,11 +20,8 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayExprs; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; 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.bvtype.BvExprs; import hu.bme.mit.theta.core.type.bvtype.BvType; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.inttype.IntExprs; @@ -35,7 +32,6 @@ import java.math.BigInteger; import java.util.Iterator; -import java.util.List; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -104,7 +100,7 @@ public static Expr cast(final Expr expr, final T type) { return result; } else { throw new ClassCastException( - "The type of expression " + expr + " is not of type " + type); + "The type of expression " + expr + " is not of type " + type + ", but " + expr.getType()); } } diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/VarCollectorStmtVisitor.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/VarCollectorStmtVisitor.java index cb4d44890d..7a1acbef33 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/VarCollectorStmtVisitor.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/VarCollectorStmtVisitor.java @@ -21,6 +21,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -64,6 +65,13 @@ public Void visit(final AssignStmt stmt, return null; } + @Override + public Void visit(MemoryAssignStmt stmt, Collection> vars) { + ExprUtils.collectVars(stmt.getDeref(), vars); + ExprUtils.collectVars(stmt.getExpr(), vars); + return null; + } + @Override public Void visit(final HavocStmt stmt, final Collection> vars) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/WpState.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/WpState.java index 72b4312ac3..f396c5ad93 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/WpState.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/WpState.java @@ -24,6 +24,7 @@ import hu.bme.mit.theta.core.stmt.HavocStmt; import hu.bme.mit.theta.core.stmt.IfStmt; import hu.bme.mit.theta.core.stmt.LoopStmt; +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt; import hu.bme.mit.theta.core.stmt.NonDetStmt; import hu.bme.mit.theta.core.stmt.OrtStmt; import hu.bme.mit.theta.core.stmt.SequenceStmt; @@ -152,6 +153,11 @@ public WpState visit(final AssignStmt stmt, return new WpState(expr, constCount); } + @Override + public WpState visit(MemoryAssignStmt stmt, WpState param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public WpState visit(final HavocStmt stmt, final WpState state) { @@ -221,6 +227,11 @@ public WpState visit(final AssignStmt stmt, return WpVisitor.getInstance().visit(stmt, state); } + @Override + public WpState visit(MemoryAssignStmt stmt, WpState param) { + throw new UnsupportedOperationException("MemoryAssignStmt not supported (yet)"); + } + @Override public WpState visit(final HavocStmt stmt, final WpState state) { diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/PushPopVarIndexing.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/PushPopVarIndexing.java deleted file mode 100644 index bb82570f86..0000000000 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/PushPopVarIndexing.java +++ /dev/null @@ -1,486 +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.core.utils.indexings; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; -import hu.bme.mit.theta.common.container.Containers; -import hu.bme.mit.theta.core.decl.VarDecl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringJoiner; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static java.lang.Math.max; - -/** - * Represents an immutable mapping, where each variable is associated with a stackable - * index. The inner builder class can also be used to create a new instance. - * NOTE: THIS CLASS IS CURRENTLY UNSTABLE. - */ -public class PushPopVarIndexing implements VarIndexing { - private static final PushPopVarIndexing ALL_ZERO = new PushPopVarIndexing.PushPopVarIndexingBuilder(0).build(); - private static final PushPopVarIndexing ALL_ONE = new PushPopVarIndexing.PushPopVarIndexingBuilder(1).build(); - - private final int defaultIndex; - private final Map, OffsetStack> varToOffset; - - private PushPopVarIndexing(final PushPopVarIndexing.PushPopVarIndexingBuilder builder) { - defaultIndex = builder.defaultIndex; - varToOffset = ImmutableMap.copyOf(builder.varToOffset); - } - - /** - * Create a new instance with a default index. - * - * @param defaultIndex Default index - * @return New instance - */ - static PushPopVarIndexing all(final int defaultIndex) { - checkArgument(defaultIndex >= 0); - switch (defaultIndex) { - case 0: - return ALL_ZERO; - case 1: - return ALL_ONE; - default: - return new PushPopVarIndexing.PushPopVarIndexingBuilder(defaultIndex).build(); - } - } - - /** - * Create a builder with a default index. - * - * @param defaultIndex Default index - * @return New builder - */ - static PushPopVarIndexing.PushPopVarIndexingBuilder builder(final int defaultIndex) { - checkArgument(defaultIndex >= 0); - return new PushPopVarIndexing.PushPopVarIndexingBuilder(defaultIndex); - } - - /** - * Create a new builder from the current instance. - * - * @return New builder - */ - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder transform() { - return new PushPopVarIndexing.PushPopVarIndexingBuilder(this); - } - - @Override - public VarIndexing inc(VarDecl varDecl, int n) { - throw new UnsupportedOperationException(""); - } - - /** - * Increment the index of a given variable by one - * - * @param varDecl Variable to increment - * @return Transformed indexing - */ - @Override - public PushPopVarIndexing inc(final VarDecl varDecl) { - checkNotNull(varDecl); - return transform().inc(varDecl).build(); - } - - /** - * Push the offset of the varDecl to stack - * - * @param varDecl Variable to push - * @return Transformed index - */ - public PushPopVarIndexing push(final VarDecl varDecl) { - return transform().push(varDecl).build(); - } - - /** - * Pop the offset of the varDecl from stack - * - * @param varDecl Variable to pop - * @return Transformed index - */ - public PushPopVarIndexing pop(final VarDecl varDecl) { - return transform().pop(varDecl).build(); - } - - /** - * Add another indexing to the current instance - * - * @param indexing Other indexing - * @return Sum of the two indexings - */ - @Override - public PushPopVarIndexing add(final VarIndexing indexing) { - checkNotNull(indexing); - return transform().add(indexing.transform()).build(); - } - - /** - * Subtract another indexing from the current instance - * - * @param indexing Other indexing - * @return Difference of the two indexings - */ - @Override - public PushPopVarIndexing sub(final VarIndexing indexing) { - checkNotNull(indexing); - return transform().sub(indexing.transform()).build(); - } - - /** - * Join another indexing to the current instance - * - * @param indexing Other indexing - * @return Joined indexing - */ - @Override - public PushPopVarIndexing join(final VarIndexing indexing) { - checkNotNull(indexing); - return transform().join(indexing.transform()).build(); - } - - /** - * Get the index of a variable - * - * @param varDecl Variable - * @return Index - */ - @Override - public int get(final VarDecl varDecl) { - checkNotNull(varDecl); - final OffsetStack current = varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - final int offset = current.peek(); - return defaultIndex + offset; - } - - public static class PushPopVarIndexingBuilder implements VarIndexingBuilder { - private int defaultIndex; - private Map, OffsetStack> varToOffset; - - private PushPopVarIndexingBuilder(final int defaultIndex) { - checkArgument(defaultIndex >= 0, "Negative default index"); - this.defaultIndex = defaultIndex; - varToOffset = Containers.createMap(); - } - - private PushPopVarIndexingBuilder(final PushPopVarIndexing indexing) { - this.defaultIndex = indexing.defaultIndex; - this.varToOffset = Containers.createMap(indexing.varToOffset); - } - - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder inc(final VarDecl varDecl) { - checkNotNull(varDecl); - - final OffsetStack current = varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - checkArgument(defaultIndex + current.peek() + 1 >= 0, "Negative index for variable"); - varToOffset.put(varDecl, current.incCurrent()); - - return this; - } - - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder incAll() { - defaultIndex = defaultIndex + 1; - return this; - } - - public PushPopVarIndexing.PushPopVarIndexingBuilder push(final VarDecl varDecl) { - final OffsetStack current = varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - varToOffset.put(varDecl, current.push()); - return this; - } - - public PushPopVarIndexing.PushPopVarIndexingBuilder pop(final VarDecl varDecl) { - final OffsetStack current = varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - varToOffset.put(varDecl, current.pop()); - return this; - } - - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder add(final VarIndexingBuilder genericThat) { - checkNotNull(genericThat); - checkArgument(genericThat instanceof PushPopVarIndexing.PushPopVarIndexingBuilder, "Only builders of the same type can be added together!"); - - PushPopVarIndexing.PushPopVarIndexingBuilder that = (PushPopVarIndexing.PushPopVarIndexingBuilder) genericThat; - - final int newDefaultIndex = this.defaultIndex + that.defaultIndex; - final Map, OffsetStack> newVarToOffset = Containers.createMap(); - - final Set> varDecls = Sets.union(this.varToOffset.keySet(), that.varToOffset.keySet()); - for (final VarDecl varDecl : varDecls) { - final OffsetStack offsetStack1 = this.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - final OffsetStack offsetStack2 = that.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - - final OffsetStack sum = offsetStack1.add(offsetStack2); - newVarToOffset.put(varDecl, sum); - } - - this.defaultIndex = newDefaultIndex; - this.varToOffset = newVarToOffset; - return this; - } - - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder sub(final VarIndexingBuilder genericThat) { - checkNotNull(genericThat); - checkArgument(genericThat instanceof PushPopVarIndexing.PushPopVarIndexingBuilder, "Only builders of the same type can be added together!"); - - PushPopVarIndexing.PushPopVarIndexingBuilder that = (PushPopVarIndexing.PushPopVarIndexingBuilder) genericThat; - - final int newDefaultIndex = this.defaultIndex - that.defaultIndex; - final Map, OffsetStack> newVarToOffset = Containers.createMap(); - - final Set> varDecls = Sets.union(this.varToOffset.keySet(), that.varToOffset.keySet()); - for (final VarDecl varDecl : varDecls) { - final OffsetStack offsetStack1 = this.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - final OffsetStack offsetStack2 = that.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - - final OffsetStack sum = offsetStack1.sub(offsetStack2); - newVarToOffset.put(varDecl, sum); - } - - this.defaultIndex = newDefaultIndex; - this.varToOffset = newVarToOffset; - return this; - } - - @Override - public PushPopVarIndexing.PushPopVarIndexingBuilder join(final VarIndexingBuilder genericThat) { - checkNotNull(genericThat); - checkArgument(genericThat instanceof PushPopVarIndexing.PushPopVarIndexingBuilder, "Only builders of the same type can be added together!"); - - PushPopVarIndexing.PushPopVarIndexingBuilder that = (PushPopVarIndexing.PushPopVarIndexingBuilder) genericThat; - - final int newDefaultIndex = max(this.defaultIndex, that.defaultIndex); - final Map, OffsetStack> newVarToOffset = Containers.createMap(); - - final Set> varDecls = Sets.union(this.varToOffset.keySet(), that.varToOffset.keySet()); - for (final VarDecl varDecl : varDecls) { - final OffsetStack offsetStack1 = this.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - final OffsetStack offsetStack2 = that.varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - - final OffsetStack sum = offsetStack1.max(offsetStack2); - newVarToOffset.put(varDecl, sum); - } - - this.defaultIndex = newDefaultIndex; - this.varToOffset = newVarToOffset; - return this; - } - - @Override - public int get(final VarDecl varDecl) { - checkNotNull(varDecl); - final OffsetStack current = varToOffset.getOrDefault(varDecl, OffsetStack.create(0)); - final int offset = current.peek(); - return defaultIndex + offset; - } - - @Override - public PushPopVarIndexing build() { - return new PushPopVarIndexing(this); - } - - } - - private static class OffsetStack { - private final int currentHeight; - private final int negativeCount; - private final List offsets; - private final int prevMax; - private final int prevMin; - - private OffsetStack(final int currentHeight, final int negativeCount, final List offsets, final int prevMax, final int prevMin) { - checkNotNull(offsets); - this.currentHeight = currentHeight; - this.negativeCount = negativeCount; - this.offsets = ImmutableList.copyOf(offsets); - this.prevMax = prevMax; - this.prevMin = prevMin; - } - - private static OffsetStack create(final int firstElement) { - final int newCurrentHeight = 0; - final List newIndices = new ArrayList<>(); - newIndices.add(firstElement); - return OffsetStack.of(newCurrentHeight, 0, newIndices, firstElement, firstElement); - } - - private static OffsetStack of(final int currentHeight, final int negativeCount, final List indices, final int prevMax, final int prevMin) { - return new OffsetStack(currentHeight, negativeCount, indices, prevMax, prevMin); - } - - private OffsetStack push() { - final int newCurrentHeight = currentHeight + 1; - final int value = prevMax + 1; - final List newIndices = new ArrayList<>(offsets); - if (newCurrentHeight > 0) { - newIndices.add(value); - } else { - newIndices.remove(negativeCount + newCurrentHeight); - newIndices.add(negativeCount + newCurrentHeight, value); - } - return OffsetStack.of(newCurrentHeight, negativeCount, newIndices, value, prevMin); - } - - private OffsetStack pop() { - final int newCurrentHeight = currentHeight - 1; - final List newIndices = new ArrayList<>(offsets); - int newNegativeCount = negativeCount; - if (newCurrentHeight >= 0) { - newIndices.remove(newIndices.size() - 1); - } else if (newNegativeCount < -newCurrentHeight) { - newIndices.add(0, 0); - ++newNegativeCount; - } - return OffsetStack.of(newCurrentHeight, newNegativeCount, newIndices, prevMax, prevMin); - } - - private OffsetStack incCurrent() { - final List newIndices = new ArrayList<>(offsets); - - newIndices.remove(negativeCount + currentHeight); - newIndices.add(negativeCount + currentHeight, prevMax + 1); - final int newPrevMax = prevMax + 1; - return OffsetStack.of(currentHeight, negativeCount, newIndices, newPrevMax, prevMin); - } - - private OffsetStack add(final OffsetStack that) { - checkArgument(this.currentHeight >= that.negativeCount, "Cannot add stacks due to mismatched depths!"); - - int i; - final List newOffsets = new ArrayList<>(this.offsets); - int newCurrentHeight = this.currentHeight; - int newPrevMax = this.prevMax; - for (i = 0; i < that.negativeCount; i++) { - final int newOffset = this.prevMax + that.offsets.get(i); - newOffsets.remove(currentHeight - (that.negativeCount - i)); - newOffsets.add(currentHeight - (that.negativeCount - i), newOffset); - newPrevMax = Math.max(newPrevMax, newOffset); - } - if (that.currentHeight >= 0) { - final int newOffset = this.prevMax + that.offsets.get(i); - newOffsets.remove(currentHeight); - newOffsets.add(currentHeight, newOffset); - newPrevMax = Math.max(newPrevMax, newOffset); - ++i; - for (; i < that.offsets.size(); ++i) { - newOffsets.add(this.prevMax + that.offsets.get(i)); - newPrevMax = Math.max(newPrevMax, that.offsets.get(i)); - ++newCurrentHeight; - } - } else { - newCurrentHeight += that.currentHeight; - for (int i1 = 0; i1 > that.currentHeight; --i1) { - newOffsets.remove(newOffsets.size() - 1); - } - } - return OffsetStack.of(newCurrentHeight, this.negativeCount, newOffsets, newPrevMax, prevMin); - } - - private OffsetStack sub(final OffsetStack that) { - int i; - final List newOffsets = new ArrayList<>(this.offsets); - int newCurrentHeight = this.currentHeight; - int newPrevMin = this.prevMin; - int newNegativeCount = negativeCount; - if (this.currentHeight < that.currentHeight) { - int toAdd = that.currentHeight - this.currentHeight; - newNegativeCount += toAdd; - for (int i1 = 0; i1 < toAdd; i1++) { - newOffsets.add(0, 0); - } - } - int savedNegativeCount = newNegativeCount; - final int toPop = Math.max(that.currentHeight, 0); - for (i = 0; i < that.negativeCount; i++) { - final int newOffset = prevMin - that.offsets.get(i); - if (savedNegativeCount + currentHeight - toPop - (that.negativeCount - i) >= 0) { - newOffsets.remove(savedNegativeCount + currentHeight - toPop - (that.negativeCount - i)); - newOffsets.add(savedNegativeCount + currentHeight - toPop - (that.negativeCount - i), newOffset); - } else { - newOffsets.add(0, newOffset); - ++newNegativeCount; - } - newPrevMin = Math.min(newPrevMin, newOffset); - } - if (that.currentHeight >= 0) { - final int newOffset = prevMin - that.offsets.get(i); - newOffsets.remove(savedNegativeCount + currentHeight - toPop); - newOffsets.add(savedNegativeCount + currentHeight - toPop, newOffset); - newPrevMin = Math.min(newPrevMin, newOffset); - ++i; - for (int i1 = toPop; i1 > 0; --i1) { - newOffsets.remove(i1); - } - newCurrentHeight -= toPop; - } - return OffsetStack.of(newCurrentHeight, newNegativeCount, newOffsets, prevMax, newPrevMin); - } - - private OffsetStack max(final OffsetStack that) { - checkState(this.offsets.size() == that.offsets.size() && this.currentHeight == that.currentHeight && this.negativeCount == that.negativeCount, "Only stacks of the same sizes can be joined!"); - final List newOffsets = new ArrayList<>(); - List integers = this.offsets; - for (int i = 0; i < integers.size(); i++) { - Integer offset = integers.get(i); - newOffsets.add(Math.max(offset, that.offsets.get(i))); - } - return OffsetStack.of(currentHeight, negativeCount, newOffsets, Math.max(prevMax, that.prevMax), Math.min(prevMin, that.prevMin)); - } - - private int peek() { - return offsets.get(negativeCount + currentHeight); - } - } - - @Override - public String toString() { - final StringJoiner sj = new StringJoiner(", ", "PushPopIndexMap(" + defaultIndex + ", ", ")"); - for (final VarDecl varDecl : varToOffset.keySet()) { - final StringBuilder sb = new StringBuilder(); - sb.append(varDecl.getName()); - sb.append(" -> "); - final OffsetStack offsetStack = varToOffset.get(varDecl); - final List offsets = offsetStack.offsets; - final StringJoiner stack = new StringJoiner(", ", "[", "]"); - for (int i = 0; i < offsets.size(); i++) { - Integer offset = offsets.get(i); - final StringBuilder innerSb = new StringBuilder(); - innerSb.append(i - offsetStack.negativeCount); - innerSb.append(": "); - innerSb.append(offset); - stack.add(innerSb.toString()); - } - sb.append(stack); - sj.add(sb); - } - return sj.toString(); - } - - //// - -} diff --git a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/VarIndexingFactory.java b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/VarIndexingFactory.java index 86309f9829..5893141c08 100644 --- a/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/VarIndexingFactory.java +++ b/subprojects/common/core/src/main/java/hu/bme/mit/theta/core/utils/indexings/VarIndexingFactory.java @@ -18,9 +18,6 @@ public class VarIndexingFactory { - public static PushPopVarIndexing pushPopIndexing(final int defaultOffset) { - return PushPopVarIndexing.all(defaultOffset); - } public static BasicVarIndexing basicVarIndexing(final int defaultOffset) { return BasicVarIndexing.all(defaultOffset); @@ -30,11 +27,6 @@ public static VarIndexing indexing(final int defaultOffset) { return basicVarIndexing(defaultOffset); } - public static PushPopVarIndexing.PushPopVarIndexingBuilder pushPopIndexingBuilder( - final int defaultOffset) { - return PushPopVarIndexing.builder(defaultOffset); - } - public static BasicVarIndexing.BasicVarIndexingBuilder basicIndexingBuilder( final int defaultOffset) { return BasicVarIndexing.builder(defaultOffset); diff --git a/subprojects/common/core/src/test/java/hu/bme/mit/theta/core/utils/PushPopVarIndexingTest.java b/subprojects/common/core/src/test/java/hu/bme/mit/theta/core/utils/PushPopVarIndexingTest.java deleted file mode 100644 index 899578d80d..0000000000 --- a/subprojects/common/core/src/test/java/hu/bme/mit/theta/core/utils/PushPopVarIndexingTest.java +++ /dev/null @@ -1,110 +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.core.utils; - -import hu.bme.mit.theta.core.decl.VarDecl; -import hu.bme.mit.theta.core.utils.indexings.PushPopVarIndexing; -import hu.bme.mit.theta.core.utils.indexings.VarIndexing; -import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory; -import org.junit.Test; - -import static hu.bme.mit.theta.core.decl.Decls.Var; -import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static org.junit.Assert.assertEquals; - -public class PushPopVarIndexingTest { - - final VarDecl x = Var("x", Int()); - final VarDecl y = Var("y", Int()); - final VarDecl z = Var("z", Int()); - - @Test - public void testPushPop() { - final VarIndexing indexing0 = VarIndexingFactory.pushPopIndexingBuilder(0).build(); - final VarIndexing indexing1 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).build(); - final VarIndexing indexing2 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).inc(x) - .build(); - final VarIndexing indexing3 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).inc(x) - .inc(x).build(); - final VarIndexing indexing4 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).inc(x) - .inc(x).pop(x).build(); - final VarIndexing indexing5 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).inc(x) - .inc(x).pop(x).inc(x).build(); - assertEquals(0, indexing0.get(x)); - assertEquals(1, indexing1.get(x)); - assertEquals(2, indexing2.get(x)); - assertEquals(3, indexing3.get(x)); - assertEquals(0, indexing4.get(x)); - assertEquals(4, indexing5.get(x)); - } - - @Test - public void testPopPush() { - final VarIndexing indexing0 = VarIndexingFactory.pushPopIndexingBuilder(0).build(); - final VarIndexing indexing1 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).build(); - final VarIndexing indexing2 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .build(); - final VarIndexing indexing3 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .inc(x).build(); - final VarIndexing indexing4 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .inc(x).push(x).build(); - final VarIndexing indexing5 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .inc(x).push(x).inc(x).build(); - assertEquals(0, indexing0.get(x)); - assertEquals(0, indexing1.get(x)); - assertEquals(1, indexing2.get(x)); - assertEquals(2, indexing3.get(x)); - assertEquals(3, indexing4.get(x)); - assertEquals(4, indexing5.get(x)); - } - - @Test - public void testAdd() { - final VarIndexing indexing0 = VarIndexingFactory.pushPopIndexingBuilder(0).build(); - final VarIndexing indexing1 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).build(); - final VarIndexing indexing2 = indexing0.add(indexing1); - assertEquals(0, indexing0.get(x)); - assertEquals(1, indexing1.get(x)); - assertEquals(1, indexing2.get(x)); - } - - @Test - public void testPopAdd() { - final VarIndexing indexing0 = VarIndexingFactory.pushPopIndexingBuilder(0).push(x).build(); - final VarIndexing indexing1 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .build(); - final PushPopVarIndexing indexing2 = (PushPopVarIndexing) indexing0.add(indexing1); - final VarIndexing indexing3 = indexing2.pop(x); - assertEquals(1, indexing0.get(x)); - assertEquals(1, indexing1.get(x)); - assertEquals(2, indexing2.get(x)); - assertEquals(0, indexing3.get(x)); - } - - @Test - public void testPopSub() { - final VarIndexing indexing0 = VarIndexingFactory.pushPopIndexingBuilder(Integer.MAX_VALUE) - .build(); - final VarIndexing indexing1 = VarIndexingFactory.pushPopIndexingBuilder(0).pop(x).inc(x) - .build(); - final PushPopVarIndexing indexing2 = (PushPopVarIndexing) indexing0.sub(indexing1); - assertEquals(Integer.MAX_VALUE, indexing0.get(x)); - assertEquals(1, indexing1.get(x)); - assertEquals(Integer.MAX_VALUE, indexing2.get(x)); - } - - -} diff --git a/subprojects/common/grammar/src/main/antlr/CommonTokens.g4 b/subprojects/common/grammar/src/main/antlr/CommonTokens.g4 index 8f93b8d3f4..132737fe5c 100644 --- a/subprojects/common/grammar/src/main/antlr/CommonTokens.g4 +++ b/subprojects/common/grammar/src/main/antlr/CommonTokens.g4 @@ -359,6 +359,18 @@ ASSIGN : 'assign' ; +MEMASSIGN + : 'memassign' + ; + +DEREF + : 'deref' + ; + +REF + : 'ref' + ; + HAVOC : 'havoc' ; diff --git a/subprojects/common/grammar/src/main/antlr/Expr.g4 b/subprojects/common/grammar/src/main/antlr/Expr.g4 index 6753a55233..eeb07df44c 100644 --- a/subprojects/common/grammar/src/main/antlr/Expr.g4 +++ b/subprojects/common/grammar/src/main/antlr/Expr.g4 @@ -149,10 +149,20 @@ primeExpr ; bvExtract - : primaryExpr + : derefExpr | LPAREN EXTRACT op=expr from=expr until=expr RPAREN ; +derefExpr + : refExpr + | LPAREN DEREF base=expr offset=expr type RPAREN + ; + +refExpr + : primaryExpr + | LPAREN REF expr type RPAREN + ; + primaryExpr : trueExpr | falseExpr diff --git a/subprojects/common/grammar/src/main/antlr/Stmt.g4 b/subprojects/common/grammar/src/main/antlr/Stmt.g4 index 1debbd3560..8e7c7f6c81 100644 --- a/subprojects/common/grammar/src/main/antlr/Stmt.g4 +++ b/subprojects/common/grammar/src/main/antlr/Stmt.g4 @@ -3,6 +3,7 @@ grammar Stmt; import Expr; stmt: assignStmt + | memAssignStmt | havocStmt | assumeStmt | skipStmt @@ -20,6 +21,10 @@ assignStmt : LPAREN ASSIGN lhs=ID value=expr RPAREN ; +memAssignStmt + : LPAREN MEMASSIGN derefExpr value=expr RPAREN + ; + havocStmt : LPAREN HAVOC lhs=ID RPAREN ; 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 61339fba2d..f896e6265a 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 @@ -31,6 +31,8 @@ import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.Type import hu.bme.mit.theta.core.type.abstracttype.* import hu.bme.mit.theta.core.type.anytype.Exprs +import hu.bme.mit.theta.core.type.anytype.Exprs.Dereference +import hu.bme.mit.theta.core.type.anytype.Exprs.Reference import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr @@ -54,6 +56,7 @@ import hu.bme.mit.theta.core.type.rattype.RatExprs import hu.bme.mit.theta.core.type.rattype.RatLitExpr import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.core.utils.TypeUtils +import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.grammar.ThrowingErrorListener import hu.bme.mit.theta.grammar.dsl.gen.ExprBaseVisitor import hu.bme.mit.theta.grammar.dsl.gen.ExprLexer @@ -750,6 +753,27 @@ class ExpressionWrapper(scope: Scope, content: String) { } } + 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 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 visitTrueExpr(ctx: TrueExprContext): TrueExpr { return BoolExprs.True() 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 b568c90ae2..f06550fdab 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 @@ -19,11 +19,13 @@ import com.google.common.base.Preconditions import hu.bme.mit.theta.common.dsl.Env import hu.bme.mit.theta.common.dsl.Scope import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt import hu.bme.mit.theta.core.stmt.SkipStmt import hu.bme.mit.theta.core.stmt.Stmt import hu.bme.mit.theta.core.stmt.Stmts import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.Dereference import hu.bme.mit.theta.core.type.booltype.BoolExprs import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.core.utils.TypeUtils @@ -103,5 +105,17 @@ class StatementWrapper(val content: String, scope: Scope) { 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") + } + } } } \ No newline at end of file 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 919db7f1c3..ff72c48eb5 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 @@ -25,7 +25,7 @@ import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.decl.ParamDecl import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.* -import hu.bme.mit.theta.core.type.anytype.Exprs.Prime +import hu.bme.mit.theta.core.type.anytype.Exprs.* import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr @@ -222,7 +222,8 @@ class ExprTest { arrayOf(Extract(bvLit1, Int(1), Int(4)), "(extract #b1010 1 4)", emptyMap>()), - ) + arrayOf(Dereference(Int(0), Int(1), Int()), "(deref 0 1 Int)", emptyMap>()), + ) } } 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 e8f1154f2d..372e1f796e 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 @@ -24,6 +24,7 @@ import hu.bme.mit.theta.core.decl.ParamDecl import hu.bme.mit.theta.core.stmt.Stmt import hu.bme.mit.theta.core.stmt.Stmts.* import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq +import hu.bme.mit.theta.core.type.anytype.Exprs.Dereference import hu.bme.mit.theta.core.type.inttype.IntExprs.Int import hu.bme.mit.theta.grammar.dsl.stmt.StatementWrapper import org.junit.Assert @@ -49,10 +50,14 @@ class StmtTest { @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))), diff --git a/subprojects/frontends/c-frontend/src/main/antlr/C.g4 b/subprojects/frontends/c-frontend/src/main/antlr/C.g4 index 3f0eac318b..af6b7ccd1c 100644 --- a/subprojects/frontends/c-frontend/src/main/antlr/C.g4 +++ b/subprojects/frontends/c-frontend/src/main/antlr/C.g4 @@ -31,7 +31,7 @@ grammar C; primaryExpression - : '__PRETTY_FUNC__' # gccPrettyFunc + : PRETTY_FUNC # gccPrettyFunc | Identifier # primaryExpressionId | Constant # primaryExpressionConstant | StringLiteral+ # primaryExpressionStrings @@ -55,9 +55,16 @@ primaryExpression // ; postfixExpression - : (primaryExpression /*| postfixExpressionExtension*/) - (postfixExpressionBrackets | postfixExpressionBraces | postfixExpressionMemberAccess - | postfixExpressionPtrMemberAccess | postfixExpressionIncrement | postfixExpressionDecrement)* + : primaryExpression (pfExprs+=postfixExpressionAccess)* + ; + +postfixExpressionAccess + : postfixExpressionBrackets + | postfixExpressionBraces + | postfixExpressionMemberAccess + | postfixExpressionPtrMemberAccess + | postfixExpressionIncrement + | postfixExpressionDecrement ; //postfixExpressionExtension @@ -109,7 +116,7 @@ unaryExpressionCast : unaryOperator castExpression ; unaryExpressionSizeOrAlignOf - : ('sizeof' | '_Alignof') '(' typeName ')' + : ('sizeof' | '_Alignof') '(' (typeName | expression) ')' ; //unaryExpressionAddressof // : '&&' Identifier @@ -120,7 +127,7 @@ unaryOperator ; castExpression - : '__extension__'? '(' typeName ')' castExpression #castExpressionCast + : '__extension__'? '(' castDeclarationSpecifierList ')' castExpression #castExpressionCast | unaryExpression #castExpressionUnaryExpression // | DigitSequence #castExpressionDigitSequence ; @@ -200,6 +207,19 @@ declarationSpecifiers2 : declarationSpecifier+ ; +// otherwise, (y*y)-2 is considered a cast +castDeclarationSpecifierList + : spec1+=castDeclarationSpecifier* spec2=typeSpecifierPointer? + ; + +castDeclarationSpecifier + : storageClassSpecifier + | typeSpecifier + | typeQualifier + | functionSpecifier + | alignmentSpecifier + ; + declarationSpecifier : storageClassSpecifier | typeSpecifierPointer @@ -236,6 +256,7 @@ typeSpecifier | 'unsigned' | '_Bool' | '_Complex' + | '__int128' | '__m128' | '__m128d' | '__m128i') # typeSpecifierSimple @@ -248,6 +269,7 @@ typeSpecifier | enumSpecifier # typeSpecifierEnum | typedefName # typeSpecifierTypedefName | '__typeof__' '(' constantExpression ')' # typeSpecifierTypeof + | typeSpecifier '(' '*' ')' '(' parameterTypeList? ')' # typeSpecifierFunctionPointer // | typeSpecifier pointer # typeSpecifierPointer ; @@ -554,9 +576,9 @@ functionDefinition //declarationList // : declaration+ // ; - +PRETTY_FUNC: '__PRETTY_FUNCTION__'; Extension: '__extension__' -> skip; // Hack to make .i files work (SV-COMP) -VoidSizeof: '(void) sizeof' -> skip; // Hack to make .i files work (SV-COMP) +//VoidSizeof: '(void)' [ \t]* 'sizeof' -> skip; // Hack to make .i files work (SV-COMP) Auto : 'auto'; Break : 'break'; Case : 'case'; @@ -933,6 +955,7 @@ LineDirective PragmaDirective : '#' Whitespace? 'pragma' Whitespace ~[\r\n]* + -> skip ; Whitespace diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/ParseContext.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/ParseContext.java index a2e75fc0bd..982e84b417 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/ParseContext.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/ParseContext.java @@ -28,7 +28,7 @@ public class ParseContext { private final FrontendMetadata metadata; private final CStmtCounter cStmtCounter; private Set arithmeticTraits = new LinkedHashSet<>(); - private ArchitectureType architecture = ArchitectureType.ILP32; + private ArchitectureType architecture = ArchitectureType.LP64; private Boolean multiThreading = false; private ArithmeticType arithmetic = ArithmeticType.efficient; diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/ArchitectureConfig.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/ArchitectureConfig.java index b78eb4003f..51c5c38f15 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/ArchitectureConfig.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/ArchitectureConfig.java @@ -42,12 +42,12 @@ private ArchitectureConfig() { * complex, if an int isn't at least twice as big as a short) */ public enum ArchitectureType { - ILP32(1, 8, 16, 32, 32, 64, 24, 8, 53, 11, 113, 15, 65), - LP64(1, 8, 16, 32, 64, 64, 24, 8, 53, 11, 113, 15, 65); + ILP32(1, 8, 16, 32, 32, 64, 128, 24, 8, 53, 11, 113, 15, 129), + LP64(1, 8, 16, 32, 64, 64, 128, 24, 8, 53, 11, 113, 15, 129); public final Map standardTypeSizes = new LinkedHashMap<>(); - ArchitectureType(int _bool, int _char, int _short, int _int, int _long, int _longlong, + ArchitectureType(int _bool, int _char, int _short, int _int, int _long, int _longlong, int __int128, int _float_significand, int _float_exponent, int _double_significand, int _double_exponent, int _longdouble_significand, int _longdouble_exponend, int _fitsall) { @@ -58,6 +58,7 @@ public enum ArchitectureType { standardTypeSizes.put("int", _int); standardTypeSizes.put("long", _long); standardTypeSizes.put("longlong", _longlong); + standardTypeSizes.put("__int128", __int128); standardTypeSizes.put("float_s", _float_significand); standardTypeSizes.put("float_e", _float_exponent); standardTypeSizes.put("double_s", _double_significand); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Dereference.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Dereference.java deleted file mode 100644 index 4ea7e4cef9..0000000000 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Dereference.java +++ /dev/null @@ -1,68 +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.frontend.transformation.grammar.expression; - -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.Type; -import hu.bme.mit.theta.core.type.UnaryExpr; - -/** - * TODO: should this really inherit from expr? - */ -public class Dereference extends UnaryExpr { - - private static final int HASH_SEED = 6988; - private static final String label = "*"; - private final T type; - - private Dereference(Expr op, T type) { - super(op); - this.type = type; - } - - public static Dereference of(Expr op, T type) { - return new Dereference<>(op, type); - } - - @Override - public T getType() { - return type; - } - - @Override - public LitExpr eval(Valuation val) { - throw new IllegalStateException( - "Reference/Dereference expressions are not meant to be evaluated!"); - } - - @Override - public UnaryExpr with(Expr op) { - return of(op, type); - } - - @Override - protected int getHashSeed() { - return HASH_SEED; - } - - @Override - public String getOperatorLabel() { - return label; - } -} 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 7af6596f64..60a7ff7609 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 @@ -18,6 +18,13 @@ 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.PostfixExpressionAccessContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionBracesContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionBracketsContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionDecrementContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionIncrementContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionMemberAccessContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.PostfixExpressionPtrMemberAccessContext; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; @@ -28,9 +35,11 @@ import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs; import hu.bme.mit.theta.core.type.abstracttype.DivExpr; import hu.bme.mit.theta.core.type.abstracttype.ModExpr; +import hu.bme.mit.theta.core.type.anytype.Dereference; +import hu.bme.mit.theta.core.type.anytype.Exprs; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; +import hu.bme.mit.theta.core.type.anytype.Reference; 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.bvtype.BvAndExpr; @@ -56,7 +65,7 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; 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.real.CReal; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct; import org.kframework.mpfr.BigFloat; import org.kframework.mpfr.BinaryMathContext; @@ -66,6 +75,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; @@ -77,6 +87,7 @@ import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Mod; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Neq; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Sub; +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; @@ -90,6 +101,7 @@ public class ExpressionVisitor extends CBaseVisitor> { private final FunctionVisitor functionVisitor; private final TypedefVisitor typedefVisitor; private final TypeVisitor typeVisitor; + 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) { @@ -100,6 +112,7 @@ public ExpressionVisitor(ParseContext parseContext, FunctionVisitor functionVisi this.typedefVisitor = typedefVisitor; this.typeVisitor = typeVisitor; this.uniqueWarningLogger = uniqueWarningLogger; + postfixVisitor = new PostfixVisitor(); } protected VarDecl getVar(String name) { @@ -463,7 +476,7 @@ public Expr visitCastExpressionUnaryExpression(CParser.CastExpressionUnaryExp @Override public Expr visitCastExpressionCast(CParser.CastExpressionCastContext ctx) { - CComplexType actualType = ctx.typeName().specifierQualifierList().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); @@ -479,9 +492,10 @@ public Expr visitUnaryExpressionSizeOrAlignOf(CParser.UnaryExpressionSizeOrAl LitExpr zero = signedInt.getNullValue(); parseContext.getMetadata().create(zero, "cType", signedInt); return zero; - } else { + } 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))); if (type.isPresent()) { @@ -494,6 +508,11 @@ public Expr visitUnaryExpressionSizeOrAlignOf(CParser.UnaryExpressionSizeOrAl parseContext.getMetadata().create(zero, "cType", signedInt); return zero; } + } 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); + return value; } } @@ -516,6 +535,7 @@ public Expr visitUnaryExpression(CParser.UnaryExpressionContext ctx) { else expr = AbstractExprs.Add(expr, type.getUnitValue()); } + parseContext.getMetadata().create(expr, "cType", type); expr = type.castTo(expr); parseContext.getMetadata().create(expr, "cType", type); Expr wrappedExpr = type.castTo(expr); @@ -565,92 +585,38 @@ public Expr visitUnaryExpressionCast(CParser.UnaryExpressionCastContext ctx) return reference((RefExpr) localAccept); case "*": type = CComplexType.getType(accept, parseContext); - checkState(type instanceof CPointer, "Dereferencing non-pointer expression is not allowed!"); - return dereference(accept, (CPointer) type); + 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 accept; } - private Expr dereference(Expr accept, CPointer type) { - checkState(!(CComplexType.getType(accept, parseContext) instanceof CReal), "Float pointers are not yet supported!", parseContext); - Dereference of = Dereference.of(accept, type.getEmbeddedType().getSmtType()); - parseContext.getMetadata().create(of, "cType", type.getEmbeddedType()); + @SuppressWarnings("unchecked") + 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()); + parseContext.getMetadata().create(of, "cType", type); return of; } private Expr reference(RefExpr accept) { - checkState(!(CComplexType.getType(accept, parseContext) instanceof CReal), "Float pointers are not yet supported!", parseContext); - Reference of = Reference.of(accept, CComplexType.getUnsignedLong(parseContext).getSmtType()); - parseContext.getMetadata().create(of, "cType", new CPointer(null, CComplexType.getType(accept, parseContext), parseContext)); - parseContext.getMetadata().create(accept, "referenced", true); - parseContext.getMetadata().create(accept, "ptrValue", of.getId()); + 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; } @Override public Expr visitPostfixExpression(CParser.PostfixExpressionContext ctx) { - checkState(ctx.postfixExpressionMemberAccess().size() == 0 || ctx.postfixExpressionBrackets().size() == 0, "Structs of arrays are not yet supported!"); - checkState(ctx.postfixExpressionPtrMemberAccess().size() == 0, "Struct pointers are not yet supported!"); - if (ctx.postfixExpressionBraces().size() == 1) { - checkState(ctx.postfixExpressionBrackets().size() == 0, "Arrays and functions are not yet supported together!"); - CParser.ArgumentExpressionListContext exprList = ctx.postfixExpressionBraces(0).argumentExpressionList(); - List arguments = exprList == null ? List.of() : exprList.assignmentExpression().stream().map(assignmentExpressionContext -> assignmentExpressionContext.accept(functionVisitor)).collect(Collectors.toList()); - CCall cCall = new CCall(ctx.primaryExpression().getText(), arguments, parseContext); - if (ctx.primaryExpression().getText().contains("pthread")) - parseContext.setMultiThreading(true); - preStatements.add(cCall); - functionVisitor.recordMetadata(ctx, cCall); - return cCall.getRet().getRef(); - } else { - Expr primary = ctx.primaryExpression().accept(this); - if (primary == null) { - return null; - } else { - int size = ctx.postfixExpressionBrackets().size(); - for (int i = 0; i < size; i++) { - CComplexType arrayType = CComplexType.getType(primary, parseContext); - checkState(arrayType instanceof CArray, "Non-array expression used as array!"); - Expr index = ctx.postfixExpressionBrackets().get(i).accept(this); - primary = ArrayReadExpr.create(primary, index); - parseContext.getMetadata().create(primary, "cType", ((CArray) arrayType).getEmbeddedType()); - } - size = ctx.postfixExpressionMemberAccess().size(); - if (size > 0) { - StringBuilder varName = new StringBuilder(ctx.primaryExpression().getText()); - for (int i = 0; i < size; i++) { - varName.append(ctx.postfixExpressionMemberAccess().get(i).getText()); - } - VarDecl var = getVar(varName.toString()); - if (var == null) return null; - primary = var.getRef(); - } - } - CComplexType type = CComplexType.getType(primary, parseContext); - - // we handle ++ and -- as if they were additions and assignments - int increment = ctx.postfixExpressionIncrement().size() - ctx.postfixExpressionDecrement().size(); - if (increment != 0) { - checkState(!(type instanceof CArray), "Raw array access not allowed!"); - Expr expr = primary; - 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()); - } - parseContext.getMetadata().create(expr, "cType", type); - expr = type.castTo(expr); - parseContext.getMetadata().create(expr, "cType", type); - CExpr cexpr; - cexpr = new CExpr(expr, parseContext); - // no need to truncate here, as left and right side types are the same - CAssignment cAssignment = new CAssignment(primary, cexpr, "=", parseContext); - postStatements.add(cAssignment); - functionVisitor.recordMetadata(ctx, cAssignment); - functionVisitor.recordMetadata(ctx, cexpr); - } - return primary; + Expr primary = ctx.primaryExpression().accept(this); + if (primary == null) { + return null; } + for (PostfixExpressionAccessContext pfExpr : ctx.pfExprs) { + primary = pfExpr.accept(postfixVisitor).apply(primary); + } + return primary; } @Override @@ -703,7 +669,7 @@ public Expr visitPrimaryExpressionConstant(CParser.PrimaryExpressionConstantC } else if (text.startsWith("0b")) { throw new UnsupportedOperationException("Binary FP constants are not yet supported!"); } else { - bigFloat = new BigFloat(text, new BinaryMathContext(significand, exponent)); + bigFloat = new BigFloat(text, new BinaryMathContext(significand - 1, exponent)); } FpLitExpr fpLitExpr = FpUtils.bigFloatToFpLitExpr(bigFloat, FpType(exponent, significand)); parseContext.getMetadata().create(fpLitExpr, "cType", type); @@ -771,4 +737,124 @@ public Expr visitPrimaryExpressionStrings(CParser.PrimaryExpressionStringsCon parseContext.getMetadata().create(ret, "cType", signedInt); return ret; } + + class PostfixVisitor extends CBaseVisitor, Expr>> { + @Override + public Function, Expr> visitPostfixExpressionBrackets(PostfixExpressionBracketsContext ctx) { + return (primary) -> { + CComplexType arrayType = CComplexType.getType(primary, parseContext); + if (arrayType instanceof CArray) { + CComplexType elemType = ((CArray) arrayType).getEmbeddedType(); + CComplexType ptrCtype = CComplexType.getUnsignedLong(parseContext); + Type ptrType = ptrCtype.getSmtType(); + Expr index = ctx.accept(ExpressionVisitor.this); + primary = dereference(primary, index, elemType); + parseContext.getMetadata().create(primary, "cType", elemType); + return primary; + } else if (arrayType instanceof CPointer) { + CComplexType elemType = ((CPointer) arrayType).getEmbeddedType(); + CComplexType ptrCtype = CComplexType.getUnsignedLong(parseContext); + Type ptrType = ptrCtype.getSmtType(); + Expr index = ctx.accept(ExpressionVisitor.this); + primary = dereference(primary, index, elemType); + parseContext.getMetadata().create(primary, "cType", elemType); + return primary; + } else { + throw new RuntimeException("Non-array expression used as array!"); + } + }; + } + + @Override + 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); + preStatements.add(cCall); + functionVisitor.recordMetadata(ctx, cCall); + return cCall.getRet().getRef(); + }; + } + + @Override + 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 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()); + parseContext.getMetadata().create(primary, "cType", embeddedType); + return primary; + }; + } + + @Override + 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(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 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()); + parseContext.getMetadata().create(primary, "cType", embeddedType); + return primary; + }; + } + + @Override + public Function, Expr> visitPostfixExpressionIncrement(PostfixExpressionIncrementContext ctx) { + return (primary) -> { + CComplexType type = CComplexType.getType(primary, parseContext); + Expr expr = primary; + expr = AbstractExprs.Add(expr, type.getUnitValue()); + parseContext.getMetadata().create(expr, "cType", type); + expr = type.castTo(expr); + parseContext.getMetadata().create(expr, "cType", type); + CExpr cexpr; + cexpr = new CExpr(expr, parseContext); + // no need to truncate here, as left and right side types are the same + CAssignment cAssignment = new CAssignment(primary, cexpr, "=", parseContext); + postStatements.add(cAssignment); + functionVisitor.recordMetadata(ctx, cAssignment); + functionVisitor.recordMetadata(ctx, cexpr); + return primary; + }; + + } + + @Override + public Function, Expr> visitPostfixExpressionDecrement(PostfixExpressionDecrementContext ctx) { + return (primary) -> { + CComplexType type = CComplexType.getType(primary, parseContext); + Expr expr = primary; + expr = AbstractExprs.Sub(expr, type.getUnitValue()); + parseContext.getMetadata().create(expr, "cType", type); + expr = type.castTo(expr); + parseContext.getMetadata().create(expr, "cType", type); + CExpr cexpr; + cexpr = new CExpr(expr, parseContext); + // no need to truncate here, as left and right side types are the same + CAssignment cAssignment = new CAssignment(primary, cexpr, "=", parseContext); + postStatements.add(cAssignment); + functionVisitor.recordMetadata(ctx, cAssignment); + functionVisitor.recordMetadata(ctx, cexpr); + return expr; + }; + } + } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Reference.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Reference.java deleted file mode 100644 index 065f76eee3..0000000000 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/expression/Reference.java +++ /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.frontend.transformation.grammar.expression; - -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.Type; -import hu.bme.mit.theta.core.type.UnaryExpr; - -import java.util.HashMap; -import java.util.Map; - -/** - * TODO: should this really inherit from expr? - */ -public class Reference extends UnaryExpr { - - private static final int HASH_SEED = 6987; - private static final String label = "&"; - private final int id; - private final R ptrType; - private static final Map, Integer> counters = new HashMap<>(); - - private Reference(Expr op, R ptrType, int id) { - super(op); - this.ptrType = ptrType; - this.id = id; - } - - public static Reference of(Expr op, R ptrType) { - if (!counters.containsKey(op)) { - counters.put(op, counters.size()); - } - return new Reference<>(op, ptrType, counters.get(op)); - } - - public static Reference of(Expr op, R ptrType, - int id) { - return new Reference<>(op, ptrType, id); - } - - @Override - public R getType() { - return ptrType; - } - - @Override - public LitExpr eval(Valuation val) { - throw new IllegalStateException( - "Reference/Dereference expressions are not meant to be evaluated!"); - } - - @Override - public UnaryExpr with(Expr op) { - return of(op, ptrType, id); - } - - @Override - protected int getHashSeed() { - return HASH_SEED; - } - - @Override - public String getOperatorLabel() { - return label; - } - - public int getId() { - return id; - } -} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java index bddc6aab05..eb98d8d707 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/function/FunctionVisitor.java @@ -22,8 +22,11 @@ import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.decl.VarDecl; +import hu.bme.mit.theta.core.model.ImmutableValuation; import hu.bme.mit.theta.core.stmt.AssumeStmt; import hu.bme.mit.theta.core.type.Expr; +import hu.bme.mit.theta.core.type.LitExpr; +import hu.bme.mit.theta.core.type.anytype.Exprs; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.frontend.ParseContext; @@ -58,7 +61,7 @@ import hu.bme.mit.theta.frontend.transformation.model.statements.CSwitch; import hu.bme.mit.theta.frontend.transformation.model.statements.CWhile; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CStruct; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; import hu.bme.mit.theta.frontend.transformation.model.types.simple.Struct; import org.antlr.v4.runtime.ParserRuleContext; @@ -66,7 +69,6 @@ import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.LinkedHashMap; @@ -78,6 +80,8 @@ import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.decl.Decls.Var; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add; +import static hu.bme.mit.theta.core.utils.TypeUtils.cast; import static hu.bme.mit.theta.grammar.UtilsKt.textWithWS; /** @@ -105,11 +109,9 @@ public void clear() { private final List> flatVariables; private final Map, CDeclaration> functions; - private Collection> createVars(CDeclaration declaration) { + private void createVars(CDeclaration declaration) { String name = declaration.getName(); - List> vars = new ArrayList<>(); - createVars(name, declaration, declaration.getActualType(), vars); - return vars; + createVars(name, declaration, declaration.getActualType()); } private String getName(final String name) { @@ -123,12 +125,8 @@ private String getName(final String name) { return sj.toString(); } - private void createVars(String name, CDeclaration declaration, CComplexType type, List> vars) { - if (type instanceof CStruct) { - ((CStruct) type).getFields().forEach((s, type1) -> { - createVars(name + "." + s, declaration, type1, vars); - }); - } + private void createVars(String name, CDeclaration declaration, CComplexType type) { +// checkState(declaration.getArrayDimensions().size() <= 1, "Currently, higher dimension arrays not supported"); Tuple2>> peek = variables.peek(); VarDecl varDecl = Var(getName(name), type.getSmtType()); if (peek.get2().containsKey(name)) { @@ -162,8 +160,8 @@ public CStatement visitCompilationUnit(CParser.CompilationUnitContext ctx) { flatVariables.clear(); functions.clear(); - ctx.accept(typedefVisitor); // ExpressionVisitor.setBitwise(ctx.accept(BitwiseChecker.instance)); + ctx.accept(typedefVisitor); List globalUsages = globalDeclUsageVisitor.getGlobalUsages(ctx); @@ -175,6 +173,11 @@ public CStatement visitCompilationUnit(CParser.CompilationUnitContext ctx) { ArithmeticType.bitvector : ArithmeticType.integer); } + Set typedefs = ctx.accept(typedefVisitor); + for (CDeclaration typedef : typedefs) { + parseContext.getMetadata().create(typedef.getName(), "cTypedefName", typedef.getActualType()); + } + CProgram program = new CProgram(parseContext); for (CParser.ExternalDeclarationContext externalDeclarationContext : globalUsages) { CStatement accept = externalDeclarationContext.accept(this); @@ -464,9 +467,24 @@ public CStatement visitBodyDeclaration(CParser.BodyDeclarationContext ctx) { } } else { checkState(declaration.getVarDecls().size() == 1, "non-struct declarations shall only have one variable!"); - CAssignment cAssignment = new CAssignment(declaration.getVarDecls().get(0).getRef(), declaration.getInitExpr(), "=", parseContext); - recordMetadata(ctx, cAssignment); - compound.getcStatementList().add(cAssignment); + if (declaration.getInitExpr() instanceof CInitializerList initializerList) { + final var ptrType = CComplexType.getUnsignedLong(parseContext); + LitExpr currentValue = ptrType.getNullValue(); + LitExpr unitValue = ptrType.getUnitValue(); + for (Tuple2, CStatement> statement : initializerList.getStatements()) { +// checkState(false, "Code here seems to be buggy"); + final var expr = statement.get2().getExpression(); + final var deref = Exprs.Dereference(cast(declaration.getVarDecls().get(0).getRef(), currentValue.getType()), cast(currentValue, currentValue.getType()), expr.getType()); + CAssignment cAssignment = new CAssignment(deref, statement.get2(), "=", parseContext); + recordMetadata(ctx, cAssignment); + compound.getcStatementList().add(cAssignment); + currentValue = Add(currentValue, unitValue).eval(ImmutableValuation.empty()); + } + } else { + CAssignment cAssignment = new CAssignment(declaration.getVarDecls().get(0).getRef(), declaration.getInitExpr(), "=", parseContext); + recordMetadata(ctx, cAssignment); + compound.getcStatementList().add(cAssignment); + } } } else { createVars(declaration); @@ -481,7 +499,7 @@ public CStatement visitBodyDeclaration(CParser.BodyDeclarationContext ctx) { } } else { VarDecl varDecl = declaration.getVarDecls().get(0); - if (!(varDecl.getType() instanceof ArrayType) && !(varDecl.getType() instanceof BoolType)) { + if (!(varDecl.getType() instanceof ArrayType) && !(varDecl.getType() instanceof BoolType) && !(CComplexType.getType(varDecl.getRef(), parseContext) instanceof CVoid)) { AssumeStmt assumeStmt = CComplexType.getType(varDecl.getRef(), parseContext).limit(varDecl.getRef()); CAssume cAssume = new CAssume(assumeStmt, parseContext); compound.getcStatementList().add(cAssume); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/GlobalDeclUsageVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/GlobalDeclUsageVisitor.java index 5552c538ad..ac26995168 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/GlobalDeclUsageVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/GlobalDeclUsageVisitor.java @@ -27,6 +27,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -53,11 +54,16 @@ public void clear() { @Override public List visitGlobalDeclaration(CParser.GlobalDeclarationContext ctx) { - List declarations = declarationVisitor.getDeclarations(ctx.declaration().declarationSpecifiers(), ctx.declaration().initDeclaratorList()); + List declarations = declarationVisitor.getDeclarations(ctx.declaration().declarationSpecifiers(), ctx.declaration().initDeclaratorList(), false); for (CDeclaration declaration : declarations) { if (!declaration.getType().isTypedef()) { + globalUsages.remove(declaration.getName()); globalUsages.put(declaration.getName(), new LinkedHashSet<>()); - usedContexts.add(Tuple2.of(declaration.getName(), ctx)); + if (usedContexts.stream().anyMatch(c -> Objects.equals(c.get1(), declaration.getName()))) { + usedContexts.replaceAll(c -> Objects.equals(c.get1(), declaration.getName()) ? Tuple2.of(declaration.getName(), ctx) : c); // keep the order, but overwrite the context + } else { + usedContexts.add(Tuple2.of(declaration.getName(), ctx)); + } current = declaration.getName(); super.visitGlobalDeclaration(ctx); current = null; @@ -87,7 +93,11 @@ public List getGlobalUsages(CParser.Compilat globalUsages.clear(); usedContexts.clear(); for (CParser.ExternalDeclarationContext externalDeclarationContext : ctx.translationUnit().externalDeclaration()) { - externalDeclarationContext.accept(this); + try { + externalDeclarationContext.accept(this); + } catch (Throwable e) { + // we don't do anything, we'll throw an error later if something's missing + } } checkState(globalUsages.containsKey("main"), "Main function not found!"); Set ret = new LinkedHashSet<>(); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/TypedefVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/TypedefVisitor.java index 2ab3fcf356..2e6e45a1d2 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/TypedefVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/preprocess/TypedefVisitor.java @@ -21,13 +21,15 @@ import hu.bme.mit.theta.frontend.transformation.grammar.type.DeclarationVisitor; import hu.bme.mit.theta.frontend.transformation.model.declaration.CDeclaration; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; import org.antlr.v4.runtime.tree.ParseTree; -import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; +import java.util.Set; -public class TypedefVisitor extends CBaseVisitor> { +public class TypedefVisitor extends CBaseVisitor> { private final DeclarationVisitor declarationVisitor; public TypedefVisitor(DeclarationVisitor declarationVisitor) { @@ -38,62 +40,78 @@ public void clear() { declarations.clear(); } - private final List declarations = new ArrayList<>(); + private final Set declarations = new LinkedHashSet<>(); public Optional getType(String id) { return declarations.stream().filter(cDeclaration -> cDeclaration.getName().equals(id)).map(CDeclaration::getActualType).findFirst(); } + public Optional getSimpleType(String id) { + return declarations.stream().filter(cDeclaration -> cDeclaration.getName().equals(id)).map(CDeclaration::getType).findFirst(); + } + @Override - public List visitDeclaration(CParser.DeclarationContext ctx) { - List ret = new ArrayList<>(); + public Set visitDeclaration(CParser.DeclarationContext ctx) { + Set ret = new LinkedHashSet<>(); if (ctx.declarationSpecifiers().declarationSpecifier(0).getText().equals("typedef")) { List declarations = declarationVisitor.getDeclarations(ctx.declarationSpecifiers(), ctx.initDeclaratorList()); + this.declarations.addAll(declarations); ret.addAll(declarations); return ret; } else return null; } @Override - public List visitBlockItemList(CParser.BlockItemListContext ctx) { - List ret = new ArrayList<>(); + public Set visitBlockItemList(CParser.BlockItemListContext ctx) { + Set ret = new LinkedHashSet<>(); for (ParseTree child : ctx.children) { - List list = child.accept(this); - if (list != null) ret.addAll(list); + Set list = child.accept(this); + if (list != null) { + this.declarations.addAll(list); + ret.addAll(list); + } } return ret; } @Override - public List visitCompoundStatement(CParser.CompoundStatementContext ctx) { - List ret = new ArrayList<>(); + public Set visitCompoundStatement(CParser.CompoundStatementContext ctx) { + Set ret = new LinkedHashSet<>(); for (ParseTree child : ctx.children) { - List list = child.accept(this); - if (list != null) ret.addAll(list); + Set list = child.accept(this); + if (list != null) { + this.declarations.addAll(list); + ret.addAll(list); + } } return ret; } @Override - public List visitGlobalDeclaration(CParser.GlobalDeclarationContext ctx) { - List ret = new ArrayList<>(); + public Set visitGlobalDeclaration(CParser.GlobalDeclarationContext ctx) { + Set ret = new LinkedHashSet<>(); if (ctx.declaration().declarationSpecifiers().declarationSpecifier(0).getText().equals("typedef")) { List declarations = declarationVisitor.getDeclarations(ctx.declaration().declarationSpecifiers(), ctx.declaration().initDeclaratorList()); ret.addAll(declarations); + this.declarations.addAll(declarations); return ret; } return null; } @Override - public List visitCompilationUnit(CParser.CompilationUnitContext ctx) { + public Set visitCompilationUnit(CParser.CompilationUnitContext ctx) { declarations.clear(); CParser.TranslationUnitContext translationUnitContext = ctx.translationUnit(); if (translationUnitContext != null) { for (CParser.ExternalDeclarationContext externalDeclarationContext : translationUnitContext.externalDeclaration()) { - List declList = externalDeclarationContext.accept(this); - if (declList != null) { - declarations.addAll(declList); + try { + Set declList = externalDeclarationContext.accept(this); + if (declList != null) { + declarations.addAll(declList); + } + } catch (Throwable e) { + // we don't do anything, we'll throw an error later if something crucial is missing } } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java index 8b70eebe3f..f752eb7ef8 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/DeclarationVisitor.java @@ -20,6 +20,7 @@ import hu.bme.mit.theta.c.frontend.dsl.gen.CParser; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; +import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.frontend.ParseContext; import hu.bme.mit.theta.frontend.transformation.grammar.expression.UnsupportedInitializer; import hu.bme.mit.theta.frontend.transformation.grammar.function.FunctionVisitor; @@ -58,6 +59,12 @@ public TypeVisitor getTypeVisitor() { return typeVisitor; } + + public List getDeclarations(CParser.DeclarationSpecifiersContext declSpecContext, + CParser.InitDeclaratorListContext initDeclContext) { + return getDeclarations(declSpecContext, initDeclContext, true); + } + /** * From a single declaration context and initialization list this function produces the * corresponding CDeclarations @@ -67,7 +74,8 @@ public TypeVisitor getTypeVisitor() { * @return the corresponding CDeclarations */ public List getDeclarations(CParser.DeclarationSpecifiersContext declSpecContext, - CParser.InitDeclaratorListContext initDeclContext) { + CParser.InitDeclaratorListContext initDeclContext, + boolean getInitExpr) { List ret = new ArrayList<>(); CSimpleType cSimpleType = declSpecContext.accept(typeVisitor); if (cSimpleType.getAssociatedName() != null) { @@ -80,7 +88,7 @@ public List getDeclarations(CParser.DeclarationSpecifiersContext d for (CParser.InitDeclaratorContext context : initDeclContext.initDeclarator()) { CDeclaration declaration = context.declarator().accept(this); CStatement initializerExpression; - if (context.initializer() != null) { + if (context.initializer() != null && getInitExpr) { if (context.initializer().initializerList() != null) { checkState( context.initializer().initializerList().designation().size() == 0, @@ -90,8 +98,9 @@ public List getDeclarations(CParser.DeclarationSpecifiersContext d try { for (CParser.InitializerContext initializer : context.initializer() .initializerList().initializers) { - CStatement expr = initializer.assignmentExpression().accept(functionVisitor); - cInitializerList.addStatement(null /* TODO: add designator */, expr); + Expr expr = cSimpleType.getActualType().castTo(initializer.assignmentExpression().accept(functionVisitor).getExpression()); + parseContext.getMetadata().create(expr, "cType", cSimpleType); + cInitializerList.addStatement(null /* TODO: add designator */, new CExpr(expr, parseContext)); } initializerExpression = cInitializerList; } catch (NullPointerException e) { diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java index 8653ac8498..83acdef4b6 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/grammar/type/TypeVisitor.java @@ -18,6 +18,9 @@ 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.CastDeclarationSpecifierContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.CastDeclarationSpecifierListContext; +import hu.bme.mit.theta.c.frontend.dsl.gen.CParser.TypeSpecifierPointerContext; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; import hu.bme.mit.theta.core.type.Expr; @@ -27,10 +30,12 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.DeclaredName; import hu.bme.mit.theta.frontend.transformation.model.types.simple.Enum; import hu.bme.mit.theta.frontend.transformation.model.types.simple.NamedType; import hu.bme.mit.theta.frontend.transformation.model.types.simple.Struct; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTree; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -60,7 +65,7 @@ public class TypeVisitor extends CBaseVisitor { private static final List standardTypes = List.of("int", "char", "long", "short", "void", "float", "double", "unsigned", "_Bool"); private static final List shorthandTypes = - List.of("long", "short", "unsigned", "_Bool"); + List.of("long", "__int128", "short", "unsigned", "_Bool"); public TypeVisitor(DeclarationVisitor declarationVisitor, TypedefVisitor typedefVisitor, ParseContext parseContext, Logger uniqueWarningLogger) { this.declarationVisitor = declarationVisitor; @@ -80,6 +85,10 @@ public CSimpleType visitDeclarationSpecifiers2(CParser.DeclarationSpecifiers2Con return createCType(ctx.declarationSpecifier()); } + @Override + public CSimpleType visitCastDeclarationSpecifierList(CastDeclarationSpecifierListContext ctx) { + return createCType(ctx.spec1, ctx.spec2); + } private CSimpleType mergeCTypes(List cSimpleTypes) { List enums = cSimpleTypes.stream().filter(cType -> cType instanceof Enum) @@ -90,18 +99,23 @@ private CSimpleType mergeCTypes(List cSimpleTypes) { cSimpleTypes.removeAll(enums); } List namedElements = cSimpleTypes.stream() - .filter(cType -> cType instanceof NamedType).collect(Collectors.toList()); - if (namedElements.size() == 0) { + .map(o -> o instanceof DeclaredName declaredName ? typedefVisitor.getSimpleType(declaredName.getDeclaredName()).orElse(o) : o) + .filter(o -> o instanceof NamedType).collect(Collectors.toList()); + if (namedElements.isEmpty()) { namedElements.add(NamedType("int", parseContext, uniqueWarningLogger)); } - NamedType mainType = (NamedType) namedElements.get(namedElements.size() - 1); - if (shorthandTypes.contains(mainType.getNamedType())) { + CSimpleType mainType = namedElements.get(namedElements.size() - 1); // if typedef, then last element is the associated name + if (mainType instanceof DeclaredName declaredName) { + mainType = typedefVisitor.getSimpleType(declaredName.getDeclaredName()).orElse(mainType); + } + + if (mainType instanceof NamedType namedType && shorthandTypes.contains(namedType.getNamedType())) { mainType = NamedType("int", parseContext, uniqueWarningLogger); } else { cSimpleTypes.remove(mainType); } - CSimpleType type = mainType.apply(cSimpleTypes); + CSimpleType type = mainType.copyOf().apply(cSimpleTypes); // we didn't get explicit signedness if (type.isSigned() == null) { if (type instanceof NamedType && ((NamedType) type).getNamedType().contains("char")) { @@ -151,7 +165,32 @@ private CSimpleType createCType( List declarationSpecifierContexts) { List cSimpleTypes = new ArrayList<>(); for (CParser.DeclarationSpecifierContext declarationSpecifierContext : declarationSpecifierContexts) { - CSimpleType ctype = declarationSpecifierContext.accept(this); + for (ParseTree child : declarationSpecifierContext.children) { + CSimpleType ctype = child.accept(this); + if (ctype != null) { + cSimpleTypes.add(ctype); + } + } + } + + return mergeCTypes(cSimpleTypes); + } + + private CSimpleType createCType( + List spec1list, + TypeSpecifierPointerContext spec2 + ) { + List cSimpleTypes = new ArrayList<>(); + for (CastDeclarationSpecifierContext declarationSpecifierContext : spec1list) { + for (ParseTree child : declarationSpecifierContext.children) { + CSimpleType ctype = child.accept(this); + if (ctype != null) { + cSimpleTypes.add(ctype); + } + } + } + if (spec2 != null) { + CSimpleType ctype = spec2.accept(this); if (ctype != null) { cSimpleTypes.add(ctype); } @@ -188,6 +227,11 @@ public CSimpleType visitTypeSpecifierCompound(CParser.TypeSpecifierCompoundConte return ctx.structOrUnionSpecifier().accept(this); } + @Override + public CSimpleType visitTypeSpecifierFunctionPointer(CParser.TypeSpecifierFunctionPointerContext ctx) { + throw new UnsupportedOperationException("Function pointers not yet implemented"); + } + @Override public CSimpleType visitCompoundDefinition(CParser.CompoundDefinitionContext ctx) { if (ctx.structOrUnion().Struct() != null) { @@ -264,6 +308,7 @@ public CSimpleType visitTypeSpecifierPointer(CParser.TypeSpecifierPointerContext return null; } for (Token star : ctx.pointer().stars) { + subtype = subtype.copyOf(); subtype.incrementPointer(); } return subtype; diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java index 6e44146aac..7c8ce9b585 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/declaration/CDeclaration.java @@ -83,8 +83,16 @@ public CSimpleType getType() { public CComplexType getActualType() { CComplexType actualType = type.getActualType(); for (CStatement arrayDimension : arrayDimensions) { - actualType = new CArray(type, actualType, actualType.getParseContext()); + CSimpleType simpleType = type.copyOf(); + simpleType.incrementPointer(); + actualType = new CArray(simpleType, actualType, actualType.getParseContext()); // some day change this back to arrays, when simple & complex types are better synchronized... } +// for (int i = 0; i < derefCounter; i++) { +// CSimpleType simpleType = type.copyOf(); +// simpleType.incrementPointer(); +// actualType = new CPointer(simpleType, actualType, actualType.getParseContext()); +// } + return actualType; } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java index 74a1df5f6b..748b919252 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CAssignment.java @@ -16,19 +16,14 @@ package hu.bme.mit.theta.frontend.transformation.model.statements; -import hu.bme.mit.theta.core.decl.VarDecl; 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; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.bvtype.BvExprs; import hu.bme.mit.theta.core.type.bvtype.BvType; import hu.bme.mit.theta.frontend.ParseContext; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -38,7 +33,6 @@ public class CAssignment extends CStatement { private final Expr lValue; private final CStatement rValue; private final String operator; - private static final Map>> memoryMaps = new LinkedHashMap<>(); public CAssignment(Expr lValue, CStatement rValue, String operator, ParseContext parseContext) { super(parseContext); @@ -60,10 +54,6 @@ public String getOperator() { return operator; } - public static Map>> getMemoryMaps() { - return memoryMaps; - } - @Override public Expr getExpression() { return lValue; @@ -76,38 +66,37 @@ public R accept(CStatementVisitor visitor, P param) { public Expr getrExpression() { Expr ret = null; + final var rExpression = rValue.getExpression(); + final var type = CComplexType.getType(lValue, parseContext); switch (operator) { case "=": - return rValue.getExpression(); + return rExpression; case "*=": - ret = AbstractExprs.Mul(lValue, rValue.getExpression()); + ret = AbstractExprs.Mul(type.castTo(lValue), type.castTo(rExpression)); break; case "/=": - ret = AbstractExprs.Div(lValue, rValue.getExpression()); + ret = AbstractExprs.Div(type.castTo(lValue), type.castTo(rExpression)); break; case "%=": - ret = AbstractExprs.Mod(lValue, rValue.getExpression()); + ret = AbstractExprs.Mod(type.castTo(lValue), type.castTo(rExpression)); break; case "+=": - ret = AbstractExprs.Add(lValue, rValue.getExpression()); + ret = AbstractExprs.Add(type.castTo(lValue), type.castTo(rExpression)); break; case "-=": - ret = AbstractExprs.Sub(lValue, rValue.getExpression()); + ret = AbstractExprs.Sub(type.castTo(lValue), type.castTo(rExpression)); break; case "^=": - Expr rExpressionXor = rValue.getExpression(); - checkState(lValue.getType() instanceof BvType && rExpressionXor.getType() instanceof BvType); - ret = BvExprs.Xor(List.of((Expr) lValue, (Expr) rExpressionXor)); + checkState(lValue.getType() instanceof BvType && rExpression.getType() instanceof BvType); + ret = BvExprs.Xor(List.of((Expr) type.castTo(lValue), (Expr) type.castTo(rExpression))); break; case "&=": - Expr rExpressionAnd = rValue.getExpression(); - checkState(lValue.getType() instanceof BvType && rExpressionAnd.getType() instanceof BvType); - ret = BvExprs.And(List.of((Expr) lValue, (Expr) rExpressionAnd)); + checkState(lValue.getType() instanceof BvType && rExpression.getType() instanceof BvType); + ret = BvExprs.And(List.of((Expr) type.castTo(lValue), (Expr) type.castTo(rExpression))); break; case "|=": - Expr rExpressionOr = rValue.getExpression(); - checkState(lValue.getType() instanceof BvType && rExpressionOr.getType() instanceof BvType); - ret = BvExprs.Or(List.of((Expr) lValue, (Expr) rExpressionOr)); + checkState(lValue.getType() instanceof BvType && rExpression.getType() instanceof BvType); + ret = BvExprs.Or(List.of((Expr) type.castTo(lValue), (Expr) type.castTo(rExpression))); break; default: throw new RuntimeException("Bad operator: " + operator); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CCall.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CCall.java index 385a25a57d..f5f36e7ee8 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CCall.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CCall.java @@ -19,7 +19,7 @@ import hu.bme.mit.theta.core.decl.VarDecl; 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.CVoid; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cint.CSignedInt; import java.util.List; import java.util.Optional; @@ -37,7 +37,7 @@ public CCall(String functionId, List params, ParseContext parseConte this.functionId = functionId; this.params = params; Optional cTypeOpt = parseContext.getMetadata().getMetadataValue(functionId, "cType"); - CComplexType type = (CComplexType) cTypeOpt.orElseGet(() -> new CVoid(null, parseContext)); + CComplexType type = (CComplexType) cTypeOpt.orElseGet(() -> new CSignedInt(null, parseContext)); ret = Var("call_" + functionId + "_ret" + counter++, type.getSmtType()); parseContext.getMetadata().create(ret.getRef(), "cType", type); } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java index 1aeec700f1..4d7670b096 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/statements/CInitializerList.java @@ -17,22 +17,14 @@ package hu.bme.mit.theta.frontend.transformation.model.statements; import hu.bme.mit.theta.common.Tuple2; -import hu.bme.mit.theta.core.model.ImmutableValuation; import hu.bme.mit.theta.core.type.Expr; -import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; 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.CArray; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add; - //TODO: designators public class CInitializerList extends CStatement { private final List, CStatement>> statements; @@ -46,7 +38,7 @@ public CInitializerList(CComplexType type, ParseContext parseContext) { @Override public Expr getExpression() { - return getTemplatedExpression(); + throw new UnsupportedOperationException("Cannot create expression of initializer list."); } @Override @@ -54,23 +46,6 @@ public R accept(CStatementVisitor visitor, P param) { return visitor.visit(this, param); } - @SuppressWarnings("unchecked") - private Expr getTemplatedExpression() { - List, Expr>> list = new ArrayList<>(); - LitExpr currentValue = (LitExpr) CComplexType.getUnsignedLong(parseContext).getNullValue(); - LitExpr unitValue = (LitExpr) CComplexType.getUnsignedLong(parseContext).getUnitValue(); - for (Tuple2, CStatement> cStatement : statements) { - Expr expr = (Expr) type.castTo(cStatement.get2().getExpression()); - list.add(Tuple2.of(currentValue, expr)); - currentValue = (LitExpr) Add(currentValue, unitValue).eval(ImmutableValuation.empty()); - } - ArrayInitExpr aie = ArrayInitExpr.of(list, - (Expr) type.getNullValue(), - (ArrayType) ArrayType.of(CComplexType.getUnsignedLong(parseContext).getSmtType(), type.getSmtType())); - parseContext.getMetadata().create(aie, "cType", new CArray(type.getOrigin(), type, parseContext)); - return aie; - } - public void addStatement(CStatement index, CStatement value) { statements.add(Tuple2.of(Optional.ofNullable(index), value)); } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java index 7ebac4f057..32b8c4b0e4 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/CComplexType.java @@ -23,6 +23,7 @@ import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.inttype.IntType; import hu.bme.mit.theta.frontend.ParseContext; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; @@ -32,6 +33,9 @@ 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.complex.integer.Fitsall; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.C128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CSigned128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CUnsigned128; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cbool.CBool; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CChar; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CSignedChar; @@ -185,6 +189,26 @@ private static CComplexType getType(Type type, ParseContext parseContext) { return new CArray(null, getType(aType.getElemType(), parseContext), parseContext); } else if (type instanceof BoolType) { return new CBool(null, parseContext); + } else if (type instanceof FpType) { + final var doubleType = FpType.of( + parseContext.getArchitecture().getBitWidth("double_e"), + parseContext.getArchitecture().getBitWidth("double_s")); + if (doubleType.equals(type)) { + return new CDouble(null, parseContext); + } + final var floatType = FpType.of( + parseContext.getArchitecture().getBitWidth("float_e"), + parseContext.getArchitecture().getBitWidth("float_s")); + if (floatType.equals(type)) { + return new CFloat(null, parseContext); + } + final var longDoubleType = FpType.of( + parseContext.getArchitecture().getBitWidth("longdouble_e"), + parseContext.getArchitecture().getBitWidth("longdouble_s")); + if (longDoubleType.equals(type)) { + return new CFloat(null, parseContext); + } + throw new RuntimeException("No suitable size found for type: " + type); } else if (type instanceof BvType) { for (Entry entry : parseContext.getArchitecture().standardTypeSizes.entrySet()) { String s = entry.getKey(); @@ -201,6 +225,8 @@ private static CComplexType getType(Type type, ParseContext parseContext) { return ((BvType) type).getSigned() ? new CSignedLong(null, parseContext) : new CUnsignedLong(null, parseContext); case "longlong": return ((BvType) type).getSigned() ? new CSignedLongLong(null, parseContext) : new CUnsignedLongLong(null, parseContext); + case "__int128": + return ((BvType) type).getSigned() ? new CSigned128(null, parseContext) : new CUnsigned128(null, parseContext); } } } @@ -231,6 +257,10 @@ public static CComplexType getSignedInt(ParseContext parseContext) { return new CSignedInt(null, parseContext); } + public static CComplexType getUnsigned128(ParseContext parseContext) { + return new CUnsigned128(null, parseContext); + } + public static CComplexType getUnsignedLongLong(ParseContext parseContext) { return new CUnsignedLongLong(null, parseContext); } @@ -243,6 +273,10 @@ public static CComplexType getUnsignedInt(ParseContext parseContext) { return new CUnsignedInt(null, parseContext); } + public static CComplexType getSigned128(ParseContext parseContext) { + return new CSigned128(null, parseContext); + } + public static CComplexType getSignedLongLong(ParseContext parseContext) { return new CSignedLongLong(null, parseContext); } @@ -312,6 +346,10 @@ public R visit(CUnsignedShort type, T param) { return visit(((CShort) type), param); } + public R visit(C128 type, T param) { + return visit(((CInteger) type), param); + } + public R visit(CLongLong type, T param) { return visit(((CInteger) type), param); } @@ -320,10 +358,18 @@ public R visit(CSignedLongLong type, T param) { return visit(((CLongLong) type), param); } + public R visit(CSigned128 type, T param) { + return visit(((C128) type), param); + } + public R visit(Fitsall type, T param) { return visit(((CInteger) type), param); } + public R visit(CUnsigned128 type, T param) { + return visit(((C128) type), param); + } + public R visit(CUnsignedLongLong type, T param) { return visit(((CLongLong) type), param); } @@ -373,7 +419,7 @@ public R visit(CCompound type, T param) { } public R visit(CArray type, T param) { - return visit(((CCompound) type), param); + return CComplexType.getUnsignedLong(type.getParseContext()).accept(this, param); } public R visit(CFunction type, T param) { @@ -381,7 +427,7 @@ public R visit(CFunction type, T param) { } public R visit(CStruct type, T param) { - return visit(((CCompound) type), param); + return CComplexType.getUnsignedLong(type.getParseContext()).accept(this, param); } public R visit(CPointer type, T param) { diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CArray.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CArray.java index 38237e6571..a746a13545 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CArray.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CArray.java @@ -18,9 +18,11 @@ 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.integer.CInteger; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.clong.CUnsignedLong; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; -public class CArray extends CCompound { +public class CArray extends CInteger { private final CComplexType embeddedType; @@ -37,4 +39,21 @@ public R accept(CComplexTypeVisitor visitor, T param) { return visitor.visit(this, param); } + + @Override + public CInteger getSignedVersion() { + return this; + } + + @Override + public CInteger getUnsignedVersion() { + return this; + } + + + @Override + public String getTypeName() { + return new CUnsignedLong(null, parseContext).getTypeName(); + } + } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CPointer.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CPointer.java index 35f91e928b..023c66b7d1 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CPointer.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CPointer.java @@ -18,9 +18,11 @@ 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.integer.CInteger; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.clong.CUnsignedLong; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; -public class CPointer extends CCompound { +public class CPointer extends CInteger { private final CComplexType embeddedType; @@ -33,7 +35,22 @@ public R accept(CComplexTypeVisitor visitor, T param) { return visitor.visit(this, param); } + @Override + public CInteger getSignedVersion() { + return this; + } + + @Override + public CInteger getUnsignedVersion() { + return this; + } + public CComplexType getEmbeddedType() { return embeddedType; } + + @Override + public String getTypeName() { + return new CUnsignedLong(null, parseContext).getTypeName(); + } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CStruct.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CStruct.java index 088461a6fe..c334a96b49 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CStruct.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/compound/CStruct.java @@ -16,17 +16,22 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.compound; +import hu.bme.mit.theta.common.Tuple2; 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.integer.CInteger; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.clong.CUnsignedLong; import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -public class CStruct extends CCompound { +public class CStruct extends CInteger { - private final Map fields; + private final List> fields; - public CStruct(CSimpleType origin, Map fields, ParseContext parseContext) { + public CStruct(CSimpleType origin, List> fields, ParseContext parseContext) { super(origin, parseContext); this.fields = fields; } @@ -35,7 +40,26 @@ public R accept(CComplexTypeVisitor visitor, T param) { return visitor.visit(this, param); } - public Map getFields() { + @Override + public CInteger getSignedVersion() { + return this; + } + + @Override + public CInteger getUnsignedVersion() { + return this; + } + + public Map getFieldsAsMap() { + return fields.stream().collect(Collectors.toMap(Tuple2::get1, Tuple2::get2)); + } + + public List> getFields() { return fields; } + + @Override + public String getTypeName() { + return new CUnsignedLong(null, parseContext).getTypeName(); + } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/C128.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/C128.java new file mode 100644 index 0000000000..e633e15f6b --- /dev/null +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/C128.java @@ -0,0 +1,50 @@ +/* + * 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.frontend.transformation.model.types.complex.integer.c128; + +import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; + +public abstract class C128 extends CInteger { + + private static final int RANK = 60; + + protected C128(CSimpleType origin, ParseContext parseContext) { + super(origin, parseContext); + rank = RANK; + } + + public R accept(CComplexTypeVisitor visitor, T param) { + return visitor.visit(this, param); + } + + @Override + public String getTypeName() { + return "__int128"; + } + + @Override + public CInteger getSignedVersion() { + return new CSigned128(null, parseContext); + } + + @Override + public CInteger getUnsignedVersion() { + return new CUnsigned128(null, parseContext); + } +} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CSigned128.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CSigned128.java new file mode 100644 index 0000000000..628c8dc643 --- /dev/null +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CSigned128.java @@ -0,0 +1,32 @@ +/* + * 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.frontend.transformation.model.types.complex.integer.c128; + +import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Signed; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; + +public class CSigned128 extends C128 implements Signed { + + public CSigned128(CSimpleType origin, ParseContext parseContext) { + super(origin, parseContext); + } + + public R accept(CComplexTypeVisitor visitor, T param) { + return visitor.visit(this, param); + } +} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CUnsigned128.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CUnsigned128.java new file mode 100644 index 0000000000..f8f161d67e --- /dev/null +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/integer/c128/CUnsigned128.java @@ -0,0 +1,33 @@ +/* + * 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.frontend.transformation.model.types.complex.integer.c128; + +import hu.bme.mit.theta.frontend.ParseContext; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Unsigned; +import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleType; + +public class CUnsigned128 extends C128 implements Unsigned { + + public CUnsigned128(CSimpleType origin, ParseContext parseContext) { + super(origin, parseContext); + unsigned = true; + } + + public R accept(CComplexTypeVisitor visitor, T param) { + return visitor.visit(this, param); + } +} diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java index 74684d57ce..ab2fd3faa7 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/CastVisitor.java @@ -24,16 +24,16 @@ import hu.bme.mit.theta.core.type.fptype.FpExprs; import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; import hu.bme.mit.theta.core.type.fptype.FpType; -import hu.bme.mit.theta.core.utils.BvUtils; 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.CVoid; -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.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Fitsall; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Signed; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Unsigned; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CSigned128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CUnsigned128; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cbool.CBool; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CSignedChar; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CUnsignedChar; @@ -50,10 +50,6 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CLongDouble; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CReal; -import java.math.BigInteger; -import java.util.List; - -import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.type.fptype.FpExprs.FromBv; import static hu.bme.mit.theta.core.type.fptype.FpExprs.ToFp; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; @@ -108,11 +104,10 @@ private Expr handleUnsignedConversion(CInteger type, Expr par } else if (that instanceof CInteger) { if (that.width() < type.width()) { if (that instanceof Signed) { - param = BvExprs.Add(List.of(BvExprs.Neg(cast(param, BvType.of(that.width()))), - BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ONE, that.width()))); + return BvExprs.SExt(cast(param, BvType.of(that.width())), + BvType.of(type.width(), false)); } - return BvExprs.ZExt(cast(param, BvType.of(that.width())), - BvType.of(type.width(), false)); + return BvExprs.ZExt(cast(param, BvType.of(that.width())), BvType.of(type.width(), false)); } else if (that.width() > type.width()) { return BvExprs.Extract(cast(param, BvType.of(that.width())), Int(0), Int(type.width())); @@ -161,6 +156,16 @@ public Expr visit(Fitsall type, Expr param) { return handleSignedConversion(type, param); } + @Override + public Expr visit(CSigned128 type, Expr param) { + return handleSignedConversion(type, param); + } + + @Override + public Expr visit(CUnsigned128 type, Expr param) { + return handleUnsignedConversion(type, param); + } + @Override public Expr visit(CSignedLongLong type, Expr param) { return handleSignedConversion(type, param); @@ -240,17 +245,4 @@ public Expr visit(CLongDouble type, Expr param) { return handleFp(type, param); } - - @Override - public Expr visit(CArray type, Expr param) { - checkState(CComplexType.getType(param, parseContext) instanceof CArray, - "Only arrays can be used in place of arrays!"); - return param.withOps(param.getOps()); - } - - - @Override - public Expr visit(CPointer type, Expr param) { - return handleUnsignedConversion((CInteger) CComplexType.getUnsignedLong(parseContext), param); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/NullValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/NullValueVisitor.java index 35a073bded..ad3983d643 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/NullValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/NullValueVisitor.java @@ -17,15 +17,11 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.bitvector; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.FpUtils; 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.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Signed; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CDouble; @@ -35,10 +31,6 @@ import org.kframework.mpfr.BinaryMathContext; import java.math.BigInteger; -import java.util.List; - -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class NullValueVisitor extends CComplexType.CComplexTypeVisitor> { private final ParseContext parseContext; @@ -53,8 +45,8 @@ public LitExpr visit(CDouble type, Void param) { new BigFloat( "0.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("double_e"), - parseContext.getArchitecture().getBitWidth("double_s"))), + parseContext.getArchitecture().getBitWidth("double_s"), + parseContext.getArchitecture().getBitWidth("double_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("double_e"), parseContext.getArchitecture().getBitWidth("double_s"))); @@ -66,8 +58,8 @@ public LitExpr visit(CFloat type, Void param) { new BigFloat( "0.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("float_e"), - parseContext.getArchitecture().getBitWidth("float_s"))), + parseContext.getArchitecture().getBitWidth("float_s"), + parseContext.getArchitecture().getBitWidth("float_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("float_e"), parseContext.getArchitecture().getBitWidth("float_s"))); @@ -79,8 +71,8 @@ public LitExpr visit(CLongDouble type, Void param) { new BigFloat( "0.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("longdouble_e"), - parseContext.getArchitecture().getBitWidth("longdouble_s"))), + parseContext.getArchitecture().getBitWidth("longdouble_s"), + parseContext.getArchitecture().getBitWidth("longdouble_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("longdouble_e"), parseContext.getArchitecture().getBitWidth("longdouble_s"))); @@ -95,16 +87,4 @@ public LitExpr visit(CInteger type, Void param) { } } - @Override - public LitExpr visit(CArray type, Void param) { - return getExpr(type); - } - - private ArrayLitExpr getExpr( - CArray type) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getNullValue(), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/TypeVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/TypeVisitor.java index 3bcac0ddec..5017a20cbf 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/TypeVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/TypeVisitor.java @@ -17,22 +17,17 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.bitvector; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.bvtype.BvType; import hu.bme.mit.theta.core.type.fptype.FpType; 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.CVoid; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; -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.complex.integer.Signed; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CDouble; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CFloat; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CLongDouble; -import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; - public class TypeVisitor extends CComplexType.CComplexTypeVisitor { private final ParseContext parseContext; @@ -61,12 +56,6 @@ public Type visit(CLongDouble type, Void param) { parseContext.getArchitecture().getBitWidth("longdouble_s")); } - @Override - public Type visit(CArray type, Void param) { - return ArrayType.of(CComplexType.getUnsignedLong(parseContext).getSmtType(), - type.getEmbeddedType().getSmtType()); - } - @Override public Type visit(CInteger type, Void param) { return BvType.of(type.width(), type instanceof Signed); @@ -77,8 +66,4 @@ public Type visit(CVoid type, Void param) { return BvType.of(1, false); } - @Override - public Type visit(CStruct type, Void param) { - return Bool(); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/UnitValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/UnitValueVisitor.java index b9b98337a4..a8b9384329 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/UnitValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/UnitValueVisitor.java @@ -17,15 +17,11 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.bitvector; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.FpUtils; 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.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Signed; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CDouble; @@ -35,10 +31,6 @@ import org.kframework.mpfr.BinaryMathContext; import java.math.BigInteger; -import java.util.List; - -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class UnitValueVisitor extends CComplexType.CComplexTypeVisitor> { private final ParseContext parseContext; @@ -53,8 +45,8 @@ public LitExpr visit(CDouble type, Void param) { new BigFloat( "1.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("double_e"), - parseContext.getArchitecture().getBitWidth("double_s"))), + parseContext.getArchitecture().getBitWidth("double_s"), + parseContext.getArchitecture().getBitWidth("double_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("double_e"), parseContext.getArchitecture().getBitWidth("double_s"))); @@ -66,8 +58,8 @@ public LitExpr visit(CFloat type, Void param) { new BigFloat( "1.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("float_e"), - parseContext.getArchitecture().getBitWidth("float_s"))), + parseContext.getArchitecture().getBitWidth("float_s"), + parseContext.getArchitecture().getBitWidth("float_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("float_e"), parseContext.getArchitecture().getBitWidth("float_s"))); @@ -79,8 +71,8 @@ public LitExpr visit(CLongDouble type, Void param) { new BigFloat( "1.0", new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("longdouble_e"), - parseContext.getArchitecture().getBitWidth("longdouble_s"))), + parseContext.getArchitecture().getBitWidth("longdouble_s"), + parseContext.getArchitecture().getBitWidth("longdouble_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("longdouble_e"), parseContext.getArchitecture().getBitWidth("longdouble_s"))); @@ -94,17 +86,4 @@ public LitExpr visit(CInteger type, Void param) { return BvUtils.bigIntegerToUnsignedBvLitExpr(BigInteger.ONE, type.width()); } } - - @Override - public LitExpr visit(CArray type, Void param) { - return getExpr(type); - } - - private ArrayLitExpr getExpr( - CArray type) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getUnitValue(), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/ValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/ValueVisitor.java index 6b0195b151..6b48e08fec 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/ValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/bitvector/ValueVisitor.java @@ -17,15 +17,11 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.bitvector; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.FpUtils; 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.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Signed; import hu.bme.mit.theta.frontend.transformation.model.types.complex.real.CDouble; @@ -35,10 +31,6 @@ import org.kframework.mpfr.BinaryMathContext; import java.math.BigInteger; -import java.util.List; - -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class ValueVisitor extends CComplexType.CComplexTypeVisitor> { private final ParseContext parseContext; @@ -53,8 +45,8 @@ public LitExpr visit(CDouble type, String param) { new BigFloat( param, new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("double_e"), - parseContext.getArchitecture().getBitWidth("double_s"))), + parseContext.getArchitecture().getBitWidth("double_s"), + parseContext.getArchitecture().getBitWidth("double_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("double_e"), parseContext.getArchitecture().getBitWidth("double_s"))); @@ -66,8 +58,8 @@ public LitExpr visit(CFloat type, String param) { new BigFloat( param, new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("float_e"), - parseContext.getArchitecture().getBitWidth("float_s"))), + parseContext.getArchitecture().getBitWidth("float_s"), + parseContext.getArchitecture().getBitWidth("float_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("float_e"), parseContext.getArchitecture().getBitWidth("float_s"))); @@ -79,8 +71,8 @@ public LitExpr visit(CLongDouble type, String param) { new BigFloat( param, new BinaryMathContext( - parseContext.getArchitecture().getBitWidth("longdouble_e"), - parseContext.getArchitecture().getBitWidth("longdouble_s"))), + parseContext.getArchitecture().getBitWidth("longdouble_s"), + parseContext.getArchitecture().getBitWidth("longdouble_e"))), FpType.of( parseContext.getArchitecture().getBitWidth("longdouble_e"), parseContext.getArchitecture().getBitWidth("longdouble_s"))); @@ -95,16 +87,4 @@ public LitExpr visit(CInteger type, String param) { } } - @Override - public LitExpr visit(CArray type, String param) { - return getExpr(type, param); - } - - private ArrayLitExpr getExpr( - CArray type, String param) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getValue(param), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/CastVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/CastVisitor.java index 7d4990f613..446f9d7439 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/CastVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/CastVisitor.java @@ -22,11 +22,11 @@ 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.CVoid; -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.integer.CInteger; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Fitsall; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.Unsigned; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CSigned128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CUnsigned128; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cbool.CBool; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CSignedChar; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CUnsignedChar; @@ -41,7 +41,6 @@ import java.math.BigInteger; -import static com.google.common.base.Preconditions.checkState; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Add; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Geq; @@ -101,6 +100,29 @@ public Expr visit(CUnsignedShort type, Expr param) { return Mod(param, Int(upperLimit)); } + + @Override + public Expr visit(CSigned128 type, Expr param) { + IteExpr sameSizeExpr = handleUnsignedSameSize(type, param); + if (sameSizeExpr != null) { + return sameSizeExpr; + } + if (true) { + return Pos(param); + } + int width = parseContext.getArchitecture().getBitWidth("__int128"); + BigInteger minValue = BigInteger.TWO.pow(width - 1).negate(); + BigInteger upperLimit = BigInteger.TWO.pow(width); + return Sub(Mod(Add(param, Int(minValue)), Int(upperLimit)), Int(minValue)); + } + + @Override + public Expr visit(CUnsigned128 type, Expr param) { + int width = parseContext.getArchitecture().getBitWidth("__int128"); + BigInteger upperLimit = BigInteger.TWO.pow(width); + return Mod(param, Int(upperLimit)); + } + @Override public Expr visit(CSignedLongLong type, Expr param) { IteExpr sameSizeExpr = handleUnsignedSameSize(type, param); @@ -204,19 +226,4 @@ public Expr visit(CBool type, Expr param) { public Expr visit(CVoid type, Expr param) { return param.withOps(param.getOps()); } - - @Override - public Expr visit(CArray type, Expr param) { - checkState(CComplexType.getType(param, parseContext) instanceof CArray, - "Only arrays can be used in place of arrays!"); - return param.withOps(param.getOps()); - } - - @Override - public Expr visit(CPointer type, Expr param) { - if (CComplexType.getType(param, parseContext) instanceof CPointer) { - return param.withOps(param.getOps()); - } - return visit((CUnsignedLong) CComplexType.getUnsignedLong(parseContext), param); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/LimitVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/LimitVisitor.java index cb5c0817c7..dcbba49ce3 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/LimitVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/LimitVisitor.java @@ -21,6 +21,8 @@ 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.integer.Fitsall; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CSigned128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CUnsigned128; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cbool.CBool; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CSignedChar; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CUnsignedChar; @@ -73,6 +75,23 @@ public AssumeStmt visit(Fitsall type, Expr param) { Leq(param, Int(BigInteger.TWO.pow(width - 1).subtract(BigInteger.ONE))))); } + @Override + public AssumeStmt visit(CSigned128 type, Expr param) { + int width = parseContext.getArchitecture().getBitWidth("__int128"); + return Assume(And( + Geq(param, Int(BigInteger.TWO.pow(width - 1).negate())), + Leq(param, Int(BigInteger.TWO.pow(width - 1).subtract(BigInteger.ONE))))); + } + + @Override + public AssumeStmt visit(CUnsigned128 type, Expr param) { + int width = parseContext.getArchitecture().getBitWidth("__int128"); + return Assume(And( + Geq(param, Int(0)), + Leq(param, Int(BigInteger.TWO.pow(width).subtract(BigInteger.ONE))))); + } + + @Override public AssumeStmt visit(CSignedLongLong type, Expr param) { int width = parseContext.getArchitecture().getBitWidth("longlong"); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/NullValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/NullValueVisitor.java index 8fca5c7c7a..9cffcc3ab6 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/NullValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/NullValueVisitor.java @@ -17,18 +17,10 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.integer; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; -import java.util.List; - -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class NullValueVisitor extends CComplexType.CComplexTypeVisitor> { @@ -38,17 +30,4 @@ public class NullValueVisitor extends CComplexType.CComplexTypeVisitor visit(CInteger type, Void param) { return Int(0); } - - @Override - public LitExpr visit(CArray type, Void param) { - return getExpr(type); - } - - private ArrayLitExpr getExpr( - CArray type) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getNullValue(), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/TypeVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/TypeVisitor.java index 5af458b62d..18a2165f3a 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/TypeVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/TypeVisitor.java @@ -17,14 +17,10 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.integer; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; -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 static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; public class TypeVisitor extends CComplexType.CComplexTypeVisitor { @@ -41,13 +37,4 @@ public Type visit(CVoid type, Void param) { return Int(); } - @Override - public Type visit(CArray type, Void param) { - return ArrayType.of(Int(), type.getEmbeddedType().getSmtType()); - } - - @Override - public Type visit(CStruct type, Void param) { - return Bool(); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/UnitValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/UnitValueVisitor.java index 9e9b250de9..6a5369a8e1 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/UnitValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/UnitValueVisitor.java @@ -17,18 +17,10 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.integer; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; -import java.util.List; - -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class UnitValueVisitor extends CComplexType.CComplexTypeVisitor> { @@ -39,16 +31,4 @@ public LitExpr visit(CInteger type, Void param) { return Int(1); } - @Override - public LitExpr visit(CArray type, Void param) { - return getExpr(type); - } - - private ArrayLitExpr getExpr( - CArray type) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getUnitValue(), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/ValueVisitor.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/ValueVisitor.java index 727096eee0..411dd16746 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/ValueVisitor.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/complex/visitors/integer/ValueVisitor.java @@ -17,19 +17,12 @@ package hu.bme.mit.theta.frontend.transformation.model.types.complex.visitors.integer; import hu.bme.mit.theta.core.type.LitExpr; -import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; -import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; -import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CArray; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInteger; import java.math.BigInteger; -import java.util.List; -import static hu.bme.mit.theta.core.type.arraytype.ArrayExprs.Array; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; -import static hu.bme.mit.theta.core.utils.TypeUtils.cast; public class ValueVisitor extends CComplexType.CComplexTypeVisitor> { @@ -40,16 +33,4 @@ public LitExpr visit(CInteger type, String param) { return Int(new BigInteger(param)); } - @Override - public LitExpr visit(CArray type, String param) { - return getExpr(type, param); - } - - private ArrayLitExpr getExpr( - CArray type, String param) { - //noinspection unchecked - ArrayType smtType = (ArrayType) type.getSmtType(); - return Array(List.of(), cast(type.getEmbeddedType().getValue(param), smtType.getElemType()), - smtType); - } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Atomic.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Atomic.java index 1856b32483..a6e4a87891 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Atomic.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Atomic.java @@ -23,6 +23,13 @@ public class Atomic extends CSimpleType { private Atomic() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Atomic(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setAtomic(true); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/CSimpleType.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/CSimpleType.java index 2c5a13e513..7dba2397ab 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/CSimpleType.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/CSimpleType.java @@ -36,6 +36,7 @@ public abstract class CSimpleType { private boolean isShort = false; private boolean isLong = false; private boolean isLongLong = false; + private boolean is128 = false; private boolean isThreadLocal = false; /** @@ -128,7 +129,7 @@ public Boolean isSigned() { return signed; } - public void setSigned(boolean signed) { + public void setSigned(Boolean signed) { this.signed = signed; } @@ -136,6 +137,14 @@ public boolean isVoid() { return false; } + public boolean is128() { + return is128; + } + + public void set128(boolean l128) { + is128 = l128; + } + public boolean isLongLong() { return isLongLong; } @@ -177,4 +186,20 @@ protected void setThreadLocal(boolean threadLocal) { public boolean isThreadLocal() { return isThreadLocal; } + + protected void setUpCopy(CSimpleType copy) { + copy.setAtomic(this.isAtomic()); + copy.setExtern(this.isExtern()); + copy.setTypedef(this.isTypedef()); + copy.setVolatile(this.isVolatile()); + copy.setSigned(this.isSigned()); + copy.setShort(this.isShort()); + copy.setLong(this.isLong()); + copy.setBool(this.isBool()); + copy.setLongLong(this.isLongLong()); + copy.set128(this.is128()); + for (int i = 0; i < this.getPointerLevel(); i++) { + copy.incrementPointer(); + } + } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/DeclaredName.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/DeclaredName.java index fc9c196809..aaa5a13970 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/DeclaredName.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/DeclaredName.java @@ -28,4 +28,15 @@ public class DeclaredName extends CSimpleType { protected void patch(CSimpleType cSimpleType) { cSimpleType.setAssociatedName(declaredName); } + + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new DeclaredName(declaredName); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + + public String getDeclaredName() { + return declaredName; + } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Enum.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Enum.java index fc9c95fee5..aa8f4e1856 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Enum.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Enum.java @@ -18,6 +18,7 @@ import hu.bme.mit.theta.core.type.Expr; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; @@ -53,4 +54,12 @@ public CSimpleType getBaseType() { anEnum.setVolatile(this.isVolatile()); return anEnum; } + + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Enum(id, new LinkedHashMap<>(fields)); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Extern.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Extern.java index d52ac3cbba..b7df17ce87 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Extern.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Extern.java @@ -23,6 +23,14 @@ public class Extern extends CSimpleType { private Extern() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Extern(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setExtern(true); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java index 6fae1e2902..2cbc83220d 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/NamedType.java @@ -22,6 +22,8 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType; import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid; import hu.bme.mit.theta.frontend.transformation.model.types.complex.compound.CPointer; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CSigned128; +import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.c128.CUnsigned128; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cbool.CBool; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CSignedChar; import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.cchar.CUnsignedChar; @@ -67,7 +69,9 @@ public CComplexType getActualType() { if (isBool()) { type = new CBool(this, parseContext); } else if (isSigned()) { - if (isLong()) { + if (is128()) { + type = new CSigned128(this, parseContext); + } else if (isLong()) { type = new CSignedLong(this, parseContext); } else if (isLongLong()) { type = new CSignedLongLong(this, parseContext); @@ -77,7 +81,9 @@ public CComplexType getActualType() { type = new CSignedInt(this, parseContext); } } else { - if (isLong()) { + if (is128()) { + type = new CUnsigned128(this, parseContext); + } else if (isLong()) { type = new CUnsignedLong(this, parseContext); } else if (isLongLong()) { type = new CUnsignedLongLong(this, parseContext); @@ -142,6 +148,9 @@ public String getNamedType() { @Override protected void patch(CSimpleType cSimpleType) { switch (namedType) { + case "__int128": + cSimpleType.set128(true); + break; case "long": if (cSimpleType.isLong()) { cSimpleType.setLongLong(true); @@ -156,6 +165,8 @@ protected void patch(CSimpleType cSimpleType) { case "_Bool": cSimpleType.setBool(true); break; + case "int": + break; default: if (!cSimpleType.isTypedef()) { throw new RuntimeException( @@ -178,6 +189,7 @@ public CSimpleType getBaseType() { namedType.setBool(this.isBool()); namedType.setLong(this.isLong()); namedType.setLongLong(this.isLongLong()); + namedType.set128(this.is128()); return namedType; } @@ -213,7 +225,12 @@ public String toString() { stringBuilder.append("long long "); } - stringBuilder.append(namedType); + if (is128()) { + stringBuilder.append("__int128"); + } else { + stringBuilder.append(namedType); + } + return stringBuilder.toString(); } @@ -225,19 +242,7 @@ public boolean isVoid() { @Override public CSimpleType copyOf() { CSimpleType namedType = new NamedType(parseContext, getNamedType(), uniqueWarningLogger); - namedType.setAtomic(this.isAtomic()); - namedType.setExtern(this.isExtern()); - namedType.setTypedef(this.isTypedef()); - namedType.setVolatile(this.isVolatile()); - namedType.setSigned(this.isSigned()); - namedType.setShort(this.isShort()); - namedType.setLong(this.isLong()); - namedType.setBool(this.isBool()); - namedType.setLongLong(this.isLongLong()); - for (int i = 0; i < this.getPointerLevel(); i++) { - namedType.incrementPointer(); - } - + setUpCopy(namedType); return namedType; } } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Signed.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Signed.java index cc8b59d1e2..5fe53c0021 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Signed.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Signed.java @@ -23,6 +23,13 @@ public class Signed extends CSimpleType { private Signed() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Signed(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setSigned(true); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java index 99df4cc0f7..eb3d8ad12f 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Struct.java @@ -16,13 +16,16 @@ package hu.bme.mit.theta.frontend.transformation.model.types.simple; +import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.logging.Logger; import hu.bme.mit.theta.common.logging.Logger.Level; 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.CStruct; +import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; public class Struct extends NamedType { @@ -60,8 +63,8 @@ public CComplexType getActualType() { return CComplexType.getSignedInt(parseContext); } currentlyBeingBuilt = true; - Map actualFields = new LinkedHashMap<>(); - fields.forEach((s, cSimpleType) -> actualFields.put(s, cSimpleType.getActualType())); + List> actualFields = new ArrayList<>(); + fields.forEach((s, cSimpleType) -> actualFields.add(Tuple2.of(s, cSimpleType.getActualType()))); currentlyBeingBuilt = false; return new CStruct(this, actualFields, parseContext); } @@ -80,7 +83,9 @@ public boolean isVoid() { public CSimpleType copyOf() { Struct struct = new Struct(name, parseContext, uniqueWarningLogger); struct.fields.putAll(fields); + setUpCopy(struct); return struct; } + } diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/ThreadLocal.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/ThreadLocal.java index 8cff9f6b4d..937d3b5cb6 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/ThreadLocal.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/ThreadLocal.java @@ -23,6 +23,13 @@ public class ThreadLocal extends CSimpleType { private ThreadLocal() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new ThreadLocal(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setThreadLocal(true); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Typedef.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Typedef.java index 51a6099409..0f2c5fece9 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Typedef.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Typedef.java @@ -23,6 +23,14 @@ public class Typedef extends CSimpleType { private Typedef() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Typedef(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setTypedef(true); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Unsigned.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Unsigned.java index c01e737e3f..809ad939e8 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Unsigned.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Unsigned.java @@ -23,6 +23,14 @@ public class Unsigned extends CSimpleType { private Unsigned() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Unsigned(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setSigned(false); diff --git a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Volatile.java b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Volatile.java index 980f60a7f8..b5cc1d8f64 100644 --- a/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Volatile.java +++ b/subprojects/frontends/c-frontend/src/main/java/hu/bme/mit/theta/frontend/transformation/model/types/simple/Volatile.java @@ -23,6 +23,14 @@ public class Volatile extends CSimpleType { private Volatile() { } + @Override + public CSimpleType copyOf() { + CSimpleType declaredNameRet = new Volatile(); + setUpCopy(declaredNameRet); + return declaredNameRet; + } + + @Override protected void patch(CSimpleType cSimpleType) { cSimpleType.setVolatile(true); diff --git a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcBackwardXcfaBuilder.java b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcBackwardXcfaBuilder.java index 7452ec27d1..159cf1eb89 100644 --- a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcBackwardXcfaBuilder.java +++ b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcBackwardXcfaBuilder.java @@ -115,7 +115,7 @@ public Object visitChc_assert(CHCParser.Chc_assertContext ctx) { } procedure = procedures.get(procName); Stmt returnTrue = AssignStmt.create(getOutParam(procedure), BoolLitExpr.of(true)); - XcfaEdge edge = new XcfaEdge(procedure.getInitLoc(), procedure.getFinalLoc().get(), new StmtLabel(returnTrue, EmptyMetaData.INSTANCE)); + XcfaEdge edge = new XcfaEdge(procedure.getInitLoc(), procedure.getFinalLoc().get(), new StmtLabel(returnTrue)); procedure.addEdge(edge); } return super.visitChc_assert(ctx); @@ -178,13 +178,13 @@ private void createCalls(XcfaProcedureBuilder builder, XcfaLocation start, XcfaL XcfaEdge callEdge = new XcfaEdge(from, middle, new InvokeLabel(calledProcedure.getName(), params, EmptyMetaData.INSTANCE)); builder.addEdge(callEdge); - XcfaEdge assertEdge = new XcfaEdge(middle, to, new StmtLabel(AssumeStmt.of(ret.getRef()), EmptyMetaData.INSTANCE)); + XcfaEdge assertEdge = new XcfaEdge(middle, to, new StmtLabel(AssumeStmt.of(ret.getRef()))); builder.addEdge(assertEdge); from = to; } Stmt returnTrue = AssignStmt.create(getOutParam(builder), BoolLitExpr.of(true)); - XcfaEdge edge = new XcfaEdge(from, end, new StmtLabel(returnTrue, EmptyMetaData.INSTANCE)); + XcfaEdge edge = new XcfaEdge(from, end, new StmtLabel(returnTrue)); builder.addEdge(edge); } } diff --git a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcForwardXcfaBuilder.java b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcForwardXcfaBuilder.java index bf86f5dcae..732d58d975 100644 --- a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcForwardXcfaBuilder.java +++ b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcForwardXcfaBuilder.java @@ -142,7 +142,7 @@ private List getIncomingAssignments(CHCParser.Chc_tailContext tail, M List> params = u_pred.symbol().stream().map(symbol -> localVars.get(symbol.getText())).toList(); localVars.values().forEach(var -> { if (!params.contains(var)) - labels.add(new StmtLabel(HavocStmt.of(var), EmptyMetaData.INSTANCE)); + labels.add(new StmtLabel(HavocStmt.of(var))); }); labels.addAll(getParamAssignments(params, from.vars)); }); @@ -174,7 +174,7 @@ private XcfaLocation getHeadTo(CHCParser.Chc_headContext head) { private List getParamAssignments(List> lhs, List> rhs) { List labels = new ArrayList<>(); for (int i = 0; i < lhs.size(); ++i) { - labels.add(new StmtLabel(AssignStmt.create(lhs.get(i), rhs.get(i).getRef()), EmptyMetaData.INSTANCE)); + labels.add(new StmtLabel(AssignStmt.create(lhs.get(i), rhs.get(i).getRef()))); } return labels; } diff --git a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcUtils.java b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcUtils.java index f9c8ab2a3a..307d6af069 100644 --- a/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcUtils.java +++ b/subprojects/frontends/chc-frontend/src/main/java/hu/bme/mit/theta/frontend/chc/ChcUtils.java @@ -87,7 +87,7 @@ public static List getTailConditionLabels(CHCParser.Chc_tailContext t } } Expr replacedExpr = changeVars(expr, varsToLocal); - labels.add(new StmtLabel(AssumeStmt.of(replacedExpr), EmptyMetaData.INSTANCE)); + labels.add(new StmtLabel(AssumeStmt.of(replacedExpr))); }); return labels; } diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java index 87dc5f2927..309976586a 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTExprTransformer.java @@ -28,6 +28,7 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayEqExpr; @@ -168,6 +169,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; + final class JavaSMTExprTransformer { private static final int CACHE_SIZE = 1000; @@ -439,6 +443,8 @@ public JavaSMTExprTransformer(final JavaSMTTransformationManager transformer, fi .addCase(ArrayInitExpr.class, this::transformArrayInit) + .addCase(Dereference.class, this::transformDereference) + .build(); } @@ -1312,6 +1318,21 @@ private Formula transformFuncApp(final FuncAppExpr expr) { } } + private Formula transformDereference(final Dereference expr) { + checkState(expr.getUniquenessIdx().isPresent(), "Incomplete dereferences (missing uniquenessIdx) are not handled properly."); + final var sort = transformer.toSort(expr.getArray().getType()); + final var sortName = expr.getArray().getType().toString() + "-" + expr.getType().toString(); + final var constSort = transformer.toSort(Int()); + final var funcDecl = context.getFormulaManager().getUFManager().declareUF( + "deref-" + sortName.replaceAll(" ", "_"), + transformer.toSort(expr.getType()), + List.of(sort, sort, constSort)); + + final var func = context.getFormulaManager().getUFManager() + .callUF(funcDecl, toTerm(expr.getArray()), toTerm(expr.getOffset()), toTerm(expr.getUniquenessIdx().get())); + return func; + } + private static Tuple2, List>> extractFuncAndArgs(final FuncAppExpr expr) { final Expr func = expr.getFunc(); final Expr arg = expr.getParam(); diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTItpSolver.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTItpSolver.java index 516021bc5e..d5546a90e3 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTItpSolver.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTItpSolver.java @@ -15,6 +15,7 @@ */ package hu.bme.mit.theta.solver.javasmt; +import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.container.Containers; import hu.bme.mit.theta.core.model.Valuation; import hu.bme.mit.theta.core.type.Expr; @@ -37,6 +38,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -51,6 +53,8 @@ final class JavaSMTItpSolver implements ItpSolver, Solver { private final InterpolatingProverEnvironment interpolatingProverEnvironment; private final Stack markers; + private final Stack, Object>> termMap; + private Map, Object> combinedTermMap = Map.of(); private final JavaSMTTermTransformer termTransformer; private final SolverContext context; @@ -68,6 +72,7 @@ public JavaSMTItpSolver(final JavaSMTSymbolTable symbolTable, this.interpolatingProverEnvironment = interpolatingProverEnvironment; markers = new StackImpl<>(); + termMap = new StackImpl<>(); } @Override @@ -88,10 +93,16 @@ public void add(final ItpMarker marker, final Expr assertion) { checkNotNull(marker); checkNotNull(assertion); checkArgument(markers.toCollection().contains(marker), "Marker not found in solver"); - final JavaSMTItpMarker z3Marker = (JavaSMTItpMarker) marker; + final JavaSMTItpMarker jsmtMarker = (JavaSMTItpMarker) marker; BooleanFormula term = (BooleanFormula) transformationManager.toTerm(assertion); - Object c = solver.add(assertion, term); - z3Marker.add(c); + if (!combinedTermMap.containsKey(assertion)) { // otherwise assertions are overridden + Object c = solver.add(assertion, term); + jsmtMarker.add(c); + termMap.add(Tuple2.of(assertion, c)); + combinedTermMap = termMap.toCollection().stream().collect(Collectors.toMap(Tuple2::get1, Tuple2::get2)); + } else { + jsmtMarker.add(combinedTermMap.get(assertion)); + } } @Override @@ -169,24 +180,29 @@ public SolverStatus check() { @Override public void push() { markers.push(); + termMap.push(); for (final JavaSMTItpMarker marker : markers) { marker.push(); } solver.push(); + combinedTermMap = termMap.toCollection().stream().collect(Collectors.toMap(Tuple2::get1, Tuple2::get2)); } @Override public void pop(final int n) { markers.pop(n); + termMap.pop(n); for (final JavaSMTItpMarker marker : markers) { marker.pop(n); } solver.pop(n); + combinedTermMap = termMap.toCollection().stream().collect(Collectors.toMap(Tuple2::get1, Tuple2::get2)); } @Override public void reset() { solver.reset(); + combinedTermMap = Map.of(); } @Override 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 cf4a63fab7..a3d7a05cd0 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 @@ -220,6 +220,14 @@ private void clearState() { unsatCore = null; } + public SolverContext getContext() { + return context; + } + + public BasicProverEnvironment getSolver() { + return solver; + } + @Override public void close() { context.close(); diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolverManager.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolverManager.java index e34221f7fa..c50859b282 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolverManager.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTSolverManager.java @@ -57,7 +57,7 @@ public SolverFactory getSolverFactory(final String name) throws InvalidConfigura final var solverName = matcher.group(1); final var solver = Solvers.valueOf(solverName); - return new ManagedFactory(JavaSMTSolverFactory.create(solver, new String[]{})); + return new ManagedFactory(JavaSMTSolverFactory.create(solver, new String[]{"--nonLinearArithmetic=USE"})); } @Override diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java index 387d31fd76..c14d63856c 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTermTransformer.java @@ -26,7 +26,6 @@ import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.LitExpr; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs; import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; @@ -82,11 +81,13 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static hu.bme.mit.theta.core.decl.Decls.Const; import static hu.bme.mit.theta.core.decl.Decls.Param; +import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Bool; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Exists; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.False; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Forall; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.True; +import static hu.bme.mit.theta.core.type.fptype.FpExprs.FpAssign; import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; import static hu.bme.mit.theta.core.type.rattype.RatExprs.Rat; @@ -114,7 +115,7 @@ public JavaSMTTermTransformer(final JavaSMTSymbolTable symbolTable, SolverContex addFunc("ite", exprTernaryOperator(hu.bme.mit.theta.core.type.anytype.IteExpr::create)); addFunc("if", exprTernaryOperator(hu.bme.mit.theta.core.type.anytype.IteExpr::create)); addFunc("prime", exprUnaryOperator(hu.bme.mit.theta.core.type.anytype.PrimeExpr::of)); - addFunc("=", exprBinaryOperator(hu.bme.mit.theta.core.type.abstracttype.AbstractExprs::Eq)); + addFunc("=", exprBinaryOperator((expr, expr2) -> expr.getType() instanceof FpType ? FpAssign((Expr) expr, (Expr) expr2) : Eq(expr, expr2))); addFunc(">=", exprBinaryOperator(hu.bme.mit.theta.core.type.abstracttype.AbstractExprs::Geq)); addFunc(">", exprBinaryOperator(hu.bme.mit.theta.core.type.abstracttype.AbstractExprs::Gt)); addFunc("<=", exprBinaryOperator(hu.bme.mit.theta.core.type.abstracttype.AbstractExprs::Leq)); @@ -249,7 +250,7 @@ public JavaSMTTermTransformer(final JavaSMTSymbolTable symbolTable, SolverContex }); environment.put(Tuple2.of("EqZero", 1), (term, args, model, vars) -> { final Expr op = transform(args.get(0), model, vars); - return AbstractExprs.Eq(op, TypeUtils.getDefaultValue(op.getType())); + return Eq(op, TypeUtils.getDefaultValue(op.getType())); }); environment.put(Tuple2.of("fp", 3), (term, args, model, vars) -> { final Expr op1 = (Expr) transform(args.get(0), model, vars); diff --git a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTUserPropagator.java b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTUserPropagator.java index 0bc2bcb02e..21557f501b 100644 --- a/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTUserPropagator.java +++ b/subprojects/solver/solver-javasmt/src/main/java/hu/bme/mit/theta/solver/javasmt/JavaSMTUserPropagator.java @@ -72,7 +72,8 @@ public void onKnownValue(final Expr expr, final boolean value) { @Override public final void onKnownValue(final BooleanFormula expr, final boolean value) { super.onKnownValue(expr, value); - final var tExpr = cast(toExpr.apply(expr), Bool()); + final var entry = registeredTerms.entrySet().stream().filter(e -> e.getValue().equals(expr)).findAny(); + final var tExpr = entry.isPresent() ? entry.get().getKey() : cast(toExpr.apply(expr), Bool()); onKnownValue(tExpr, value); } @@ -125,6 +126,11 @@ public final void propagateConflict(final List> exprs) { public final void propagateConsequence(final List> exprs, final Expr consequence) { final var terms = exprs.stream().map(registeredTerms::get).toArray(BooleanFormula[]::new); + for (var expr : exprs) { + if (registeredTerms.get(expr) == null) { + System.err.println(expr); + } + } checkState(Arrays.stream(terms).noneMatch(Objects::isNull), "Registered terms failed to look up one or more expressions from %s. Registered terms: %s", exprs, registeredTerms.keySet()); final BooleanFormula consequenceTerm = (BooleanFormula) toTerm.apply(consequence); getBackend().propagateConsequence(terms, consequenceTerm); diff --git a/subprojects/solver/solver-javasmt/src/test/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTransformerTest.java b/subprojects/solver/solver-javasmt/src/test/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTransformerTest.java index 74a46f5b8f..dc927846e2 100644 --- a/subprojects/solver/solver-javasmt/src/test/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTransformerTest.java +++ b/subprojects/solver/solver-javasmt/src/test/java/hu/bme/mit/theta/solver/javasmt/JavaSMTTransformerTest.java @@ -20,10 +20,13 @@ import hu.bme.mit.theta.common.OsHelper.OperatingSystem; 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; import hu.bme.mit.theta.core.type.arraytype.ArrayInitExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayLitExpr; +import hu.bme.mit.theta.core.type.booltype.BoolType; import hu.bme.mit.theta.core.type.booltype.QuantifiedExpr; import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.fptype.FpExprs; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.rattype.RatType; @@ -42,10 +45,10 @@ import java.util.Collection; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.stream.Collectors; -import static hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Eq; import static hu.bme.mit.theta.core.type.booltype.BoolExprs.Not; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeFalse; @@ -115,12 +118,14 @@ public void testRoundtripTransformer() throws Exception { throw e; // for functions, we don't want the solver to step in } try (final var solver = JavaSMTSolverFactory.create(this.solver, new String[]{}).createSolver()) { + BiFunction> eq = expr.getType() instanceof FpType ? FpExprs::FpAssign : AbstractExprs::Eq; + solver.push(); - solver.add(Eq(expr, expExpr)); + solver.add(eq.apply(expr, expExpr)); Assert.assertTrue("(= %s %s) is unsat\n".formatted(expr, expExpr), solver.check().isSat()); solver.pop(); solver.push(); - solver.add(Not(Eq(expr, expExpr))); + solver.add(Not(eq.apply(expr, expExpr))); Assert.assertTrue("(not (= %s %s)) is sat with model %s\n".formatted(expr, expExpr, solver.check().isSat() ? solver.getModel() : ""), solver.check().isUnsat()); solver.pop(); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java index ee9658e3ac..1233550ba0 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibExprTransformer.java @@ -28,6 +28,7 @@ import hu.bme.mit.theta.core.dsl.DeclSymbol; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayEqExpr; @@ -147,6 +148,8 @@ import java.util.List; import java.util.concurrent.ExecutionException; +import static com.google.common.base.Preconditions.checkState; + public class GenericSmtLibExprTransformer implements SmtLibExprTransformer { private static final int CACHE_SIZE = 1000; @@ -162,9 +165,11 @@ public GenericSmtLibExprTransformer(final SmtLibTransformationManager transforme this.env = new Env(); this.exprToTerm = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(); + this.table = buildDispatchTable(DispatchTable.builder()).build(); + } - this.table = DispatchTable.builder() - + protected DispatchTable.Builder buildDispatchTable(DispatchTable.Builder builder) { + builder // General .addCase(RefExpr.class, this::transformRef) @@ -399,7 +404,11 @@ public GenericSmtLibExprTransformer(final SmtLibTransformationManager transforme .addCase(ArrayInitExpr.class, this::transformArrayInit) - .build(); + // References + .addCase(Dereference.class, this::transformDereference) + + ; + return builder; } @Override @@ -1201,4 +1210,9 @@ protected String transformArrayInit(final ArrayInitExpr expr) { } return running; } + + private String transformDereference(final Dereference expr) { + checkState(expr.getUniquenessIdx().isPresent(), "Incomplete dereferences (missing uniquenessIdx) are not handled properly."); + return "(deref %s %s %s)".formatted(toTerm(expr.getArray()), toTerm(expr.getOffset()), toTerm(expr.getUniquenessIdx().get())); + } } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java index 267ee7e210..f003780f0f 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibSymbolTable.java @@ -27,7 +27,7 @@ public class GenericSmtLibSymbolTable implements SmtLibSymbolTable { - private static final String problematicCharactersRegex = ":"; + private static final String problematicCharactersRegex = "[:#]"; private static final String problematicCharactersReplacement = "\\$"; private final BiMap, String> constToSymbol; diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java index e443b7fed7..b7192786ef 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTermTransformer.java @@ -496,6 +496,9 @@ private

Expr createFuncAppExpr(final String funcExpr = checkNotNull(symbolTable.getConst(funName).getRef()); } else { final var funDefImpl = model.getTerm(funName); + if (funDefImpl == null) { + throw new SmtLibSolverException("Model (%s) does not have function \"%s\".".formatted(model, funName)); + } funcExpr = toFuncLitExpr(funDefImpl, model); } diff --git a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java index 87935b7243..ff6c8061d6 100644 --- a/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java +++ b/subprojects/solver/solver-smtlib/src/main/java/hu/bme/mit/theta/solver/smtlib/impl/generic/GenericSmtLibTypeTransformer.java @@ -45,14 +45,18 @@ public GenericSmtLibTypeTransformer(final SmtLibTransformationManager transforme typeToSmtLib = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).build(); - table = DispatchTable.builder() + table = buildDispatchTable(DispatchTable.builder()).build(); + } + + protected DispatchTable.Builder buildDispatchTable(DispatchTable.Builder builder) { + builder .addCase(BoolType.class, this::boolType) .addCase(IntType.class, this::intType) .addCase(RatType.class, this::ratType) .addCase(BvType.class, this::bvType) .addCase(FpType.class, this::fpType) - .addCase(ArrayType.class, this::arrayType) - .build(); + .addCase(ArrayType.class, this::arrayType); + return builder; } @Override diff --git a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java index 45d1f60388..fdf8e4ac50 100644 --- a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java +++ b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3ExprTransformer.java @@ -23,6 +23,7 @@ import com.microsoft.z3legacy.Context; import com.microsoft.z3legacy.FPExpr; import com.microsoft.z3legacy.FPSort; +import com.microsoft.z3legacy.Sort; import hu.bme.mit.theta.common.DispatchTable; import hu.bme.mit.theta.common.Tuple2; import hu.bme.mit.theta.common.dsl.Env; @@ -32,6 +33,7 @@ import hu.bme.mit.theta.core.dsl.DeclSymbol; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; +import hu.bme.mit.theta.core.type.anytype.Dereference; import hu.bme.mit.theta.core.type.anytype.IteExpr; import hu.bme.mit.theta.core.type.anytype.RefExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayEqExpr; @@ -149,6 +151,9 @@ import java.util.concurrent.ExecutionException; import java.util.stream.Stream; +import static com.google.common.base.Preconditions.checkState; +import static hu.bme.mit.theta.core.type.inttype.IntExprs.Int; + final class Z3ExprTransformer { private static final int CACHE_SIZE = 1000; @@ -403,6 +408,10 @@ public Z3ExprTransformer(final Z3TransformationManager transformer, final Contex .addCase(ArrayInitExpr.class, this::transformArrayInit) + // dereference + + .addCase(Dereference.class, this::transformDereference) + .build(); } @@ -1248,6 +1257,15 @@ private com.microsoft.z3legacy.Expr transformFuncApp(final FuncAppExpr exp } } + private com.microsoft.z3legacy.Expr transformDereference(final Dereference expr) { + checkState(expr.getUniquenessIdx().isPresent(), "Incomplete dereferences (missing uniquenessIdx) are not handled properly."); + final var sort = transformer.toSort(expr.getArray().getType()); + final var constSort = transformer.toSort(Int()); + final var func = context.mkFuncDecl("deref", new Sort[]{sort, sort, constSort}, transformer.toSort(expr.getType())); + return context.mkApp(func, toTerm(expr.getArray()), toTerm(expr.getOffset()), toTerm(expr.getUniquenessIdx().get())); + } + + public void reset() { exprToTerm.invalidateAll(); } diff --git a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3TermTransformer.java b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3TermTransformer.java index 61b4a9dad3..3735daa6ce 100644 --- a/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3TermTransformer.java +++ b/subprojects/solver/solver-z3-legacy/src/main/java/hu/bme/mit/theta/solver/z3legacy/Z3TermTransformer.java @@ -30,14 +30,11 @@ import hu.bme.mit.theta.core.decl.ParamDecl; import hu.bme.mit.theta.core.type.Expr; import hu.bme.mit.theta.core.type.Type; -import hu.bme.mit.theta.core.type.abstracttype.AddExpr; +import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs; import hu.bme.mit.theta.core.type.abstracttype.EqExpr; -import hu.bme.mit.theta.core.type.abstracttype.GeqExpr; -import hu.bme.mit.theta.core.type.abstracttype.GtExpr; -import hu.bme.mit.theta.core.type.abstracttype.LeqExpr; -import hu.bme.mit.theta.core.type.abstracttype.LtExpr; -import hu.bme.mit.theta.core.type.abstracttype.MulExpr; +import hu.bme.mit.theta.core.type.anytype.Exprs; import hu.bme.mit.theta.core.type.anytype.IteExpr; +import hu.bme.mit.theta.core.type.anytype.PrimeExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr; import hu.bme.mit.theta.core.type.arraytype.ArrayType; import hu.bme.mit.theta.core.type.arraytype.ArrayWriteExpr; @@ -49,13 +46,73 @@ import hu.bme.mit.theta.core.type.booltype.NotExpr; import hu.bme.mit.theta.core.type.booltype.OrExpr; import hu.bme.mit.theta.core.type.booltype.TrueExpr; +import hu.bme.mit.theta.core.type.booltype.XorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAddExpr; +import hu.bme.mit.theta.core.type.bvtype.BvAndExpr; +import hu.bme.mit.theta.core.type.bvtype.BvArithShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvConcatExpr; +import hu.bme.mit.theta.core.type.bvtype.BvExtractExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLitExpr; +import hu.bme.mit.theta.core.type.bvtype.BvLogicShiftRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvMulExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNegExpr; +import hu.bme.mit.theta.core.type.bvtype.BvNotExpr; +import hu.bme.mit.theta.core.type.bvtype.BvOrExpr; +import hu.bme.mit.theta.core.type.bvtype.BvPosExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvRotateRightExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSExtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSLtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSModExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSRemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvShiftLeftExpr; +import hu.bme.mit.theta.core.type.bvtype.BvSubExpr; +import hu.bme.mit.theta.core.type.bvtype.BvType; +import hu.bme.mit.theta.core.type.bvtype.BvUDivExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvUGtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULeqExpr; +import hu.bme.mit.theta.core.type.bvtype.BvULtExpr; +import hu.bme.mit.theta.core.type.bvtype.BvURemExpr; +import hu.bme.mit.theta.core.type.bvtype.BvXorExpr; +import hu.bme.mit.theta.core.type.bvtype.BvZExtExpr; +import hu.bme.mit.theta.core.type.fptype.FpAbsExpr; +import hu.bme.mit.theta.core.type.fptype.FpAddExpr; +import hu.bme.mit.theta.core.type.fptype.FpDivExpr; +import hu.bme.mit.theta.core.type.fptype.FpEqExpr; +import hu.bme.mit.theta.core.type.fptype.FpFromBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpGeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpGtExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsInfiniteExpr; +import hu.bme.mit.theta.core.type.fptype.FpIsNanExpr; +import hu.bme.mit.theta.core.type.fptype.FpLeqExpr; +import hu.bme.mit.theta.core.type.fptype.FpLitExpr; +import hu.bme.mit.theta.core.type.fptype.FpLtExpr; +import hu.bme.mit.theta.core.type.fptype.FpMaxExpr; +import hu.bme.mit.theta.core.type.fptype.FpMinExpr; +import hu.bme.mit.theta.core.type.fptype.FpMulExpr; +import hu.bme.mit.theta.core.type.fptype.FpNegExpr; +import hu.bme.mit.theta.core.type.fptype.FpPosExpr; +import hu.bme.mit.theta.core.type.fptype.FpRemExpr; +import hu.bme.mit.theta.core.type.fptype.FpRoundToIntegralExpr; import hu.bme.mit.theta.core.type.fptype.FpRoundingMode; +import hu.bme.mit.theta.core.type.fptype.FpSqrtExpr; +import hu.bme.mit.theta.core.type.fptype.FpSubExpr; +import hu.bme.mit.theta.core.type.fptype.FpToBvExpr; +import hu.bme.mit.theta.core.type.fptype.FpToFpExpr; import hu.bme.mit.theta.core.type.fptype.FpType; import hu.bme.mit.theta.core.type.functype.FuncType; import hu.bme.mit.theta.core.type.inttype.IntDivExpr; +import hu.bme.mit.theta.core.type.inttype.IntExprs; +import hu.bme.mit.theta.core.type.inttype.IntLitExpr; import hu.bme.mit.theta.core.type.inttype.IntModExpr; +import hu.bme.mit.theta.core.type.inttype.IntRemExpr; import hu.bme.mit.theta.core.type.inttype.IntToRatExpr; -import hu.bme.mit.theta.core.type.rattype.RatDivExpr; +import hu.bme.mit.theta.core.type.inttype.IntType; import hu.bme.mit.theta.core.type.rattype.RatToIntExpr; import hu.bme.mit.theta.core.utils.BvUtils; import hu.bme.mit.theta.core.utils.FpUtils; @@ -67,10 +124,13 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -98,36 +158,182 @@ final class Z3TermTransformer { private static final String PARAM_NAME_FORMAT = "_p%d"; private final Z3SymbolTable symbolTable; - private final Map>, Expr>> environment; + private final Map, TriFunction>, Expr>> environment; public Z3TermTransformer(final Z3SymbolTable symbolTable) { this.symbolTable = symbolTable; environment = Containers.createMap(); - environment.put("true", exprNullaryOperator(TrueExpr::getInstance)); - environment.put("false", exprNullaryOperator(FalseExpr::getInstance)); - environment.put("not", exprUnaryOperator(NotExpr::create)); - environment.put("or", exprMultiaryOperator(OrExpr::create)); - environment.put("and", exprMultiaryOperator(AndExpr::create)); - environment.put("=>", exprBinaryOperator(ImplyExpr::create)); - environment.put("iff", exprBinaryOperator(IffExpr::create)); - environment.put("=", exprBinaryOperator(EqExpr::create2)); - environment.put("<=", exprBinaryOperator(LeqExpr::create2)); - environment.put("<", exprBinaryOperator(LtExpr::create2)); - environment.put(">=", exprBinaryOperator(GeqExpr::create2)); - environment.put(">", exprBinaryOperator(GtExpr::create2)); - environment.put("+", exprMultiaryOperator(AddExpr::create2)); - environment.put("*", exprMultiaryOperator(MulExpr::create2)); - environment.put("div", exprBinaryOperator(IntDivExpr::create)); - environment.put("/", exprBinaryOperator(RatDivExpr::create)); - environment.put("if", exprTernaryOperator(IteExpr::create)); - environment.put("select", exprBinaryOperator(ArrayReadExpr::create)); - environment.put("store", exprTernaryOperator(ArrayWriteExpr::create)); - environment.put("to_real", exprUnaryOperator(IntToRatExpr::create)); - environment.put("to_int", exprUnaryOperator(RatToIntExpr::create)); - environment.put("mod", exprBinaryOperator(IntModExpr::create)); + + this.addFunc("deref", dereference()); + this.addFunc("ref", reference()); + this.addFunc("and", this.exprMultiaryOperator(AndExpr::create)); + this.addFunc("false", this.exprNullaryOperator(FalseExpr::getInstance)); + this.addFunc("true", this.exprNullaryOperator(TrueExpr::getInstance)); + this.addFunc("iff", this.exprBinaryOperator(IffExpr::create)); + this.addFunc("not", this.exprUnaryOperator(NotExpr::create)); + this.addFunc("=>", this.exprBinaryOperator(ImplyExpr::create)); + this.addFunc("xor", this.exprBinaryOperator(XorExpr::create)); + this.addFunc("or", this.exprMultiaryOperator(OrExpr::create)); + this.addFunc("ite", this.exprTernaryOperator(IteExpr::create)); + this.addFunc("if", this.exprTernaryOperator(IteExpr::create)); + this.addFunc("prime", this.exprUnaryOperator(PrimeExpr::of)); + this.addFunc("=", this.exprBinaryOperator(AbstractExprs::Eq)); + this.addFunc(">=", this.exprBinaryOperator(AbstractExprs::Geq)); + this.addFunc(">", this.exprBinaryOperator(AbstractExprs::Gt)); + this.addFunc("<=", this.exprBinaryOperator(AbstractExprs::Leq)); + this.addFunc("<", this.exprBinaryOperator(AbstractExprs::Lt)); + this.addFunc("+", this.exprBinaryOperator(AbstractExprs::Add)); + this.addFunc("+", this.exprMultiaryOperator(AbstractExprs::Add)); + this.addFunc("-", this.exprBinaryOperator(AbstractExprs::Sub)); + this.addFunc("+", this.exprUnaryOperator(AbstractExprs::Pos)); + this.addFunc("-", this.exprUnaryOperator(AbstractExprs::Neg)); + this.addFunc("*", this.exprBinaryOperator(AbstractExprs::Mul)); + this.addFunc("*", this.exprMultiaryOperator(AbstractExprs::Mul)); + this.addFunc("/", this.exprBinaryOperator(AbstractExprs::Div)); + this.addFunc("to_real", this.exprUnaryOperator(IntToRatExpr::create)); + this.addFunc("to_int", this.exprUnaryOperator(RatToIntExpr::create)); + this.addFunc("div", this.exprBinaryOperator(IntDivExpr::create)); + this.addFunc("to_rat", this.exprUnaryOperator(IntToRatExpr::create)); + this.addFunc("mod", this.exprBinaryOperator(IntModExpr::create)); + this.addFunc("rem", this.exprBinaryOperator(IntRemExpr::create)); + this.addFunc("fp.add", this.exprFpMultiaryOperator(FpAddExpr::create)); + this.addFunc("fp.sub", this.exprFpBinaryOperator(FpSubExpr::create)); + this.addFunc("fp.pos", this.exprUnaryOperator(FpPosExpr::create)); + this.addFunc("fp.neg", this.exprUnaryOperator(FpNegExpr::create)); + this.addFunc("fp.mul", this.exprFpMultiaryOperator(FpMulExpr::create)); + this.addFunc("fp.div", this.exprFpBinaryOperator(FpDivExpr::create)); + this.addFunc("fp.rem", this.exprBinaryOperator(FpRemExpr::create)); + this.addFunc("fprem", this.exprBinaryOperator(FpRemExpr::create)); + this.addFunc("fp.abs", this.exprUnaryOperator(FpAbsExpr::create)); + this.addFunc("fp.leq", this.exprBinaryOperator(FpLeqExpr::create)); + this.addFunc("fp.lt", this.exprBinaryOperator(FpLtExpr::create)); + this.addFunc("fp.geq", this.exprBinaryOperator(FpGeqExpr::create)); + this.addFunc("fp.gt", this.exprBinaryOperator(FpGtExpr::create)); + this.addFunc("fp.eq", this.exprBinaryOperator(FpEqExpr::create)); + this.addFunc("fp.isnan", this.exprUnaryOperator(FpIsNanExpr::create)); + this.addFunc("fp.isNaN", this.exprUnaryOperator(FpIsNanExpr::create)); + this.addFunc("isinfinite", this.exprUnaryOperator(FpIsInfiniteExpr::create)); + this.addFunc("fp.isInfinite", this.exprUnaryOperator(FpIsInfiniteExpr::create)); + this.addFunc("fp.roundtoint", this.exprFpUnaryOperator(FpRoundToIntegralExpr::create)); + this.addFunc("fp.roundToIntegral", this.exprFpUnaryOperator(FpRoundToIntegralExpr::create)); + this.addFunc("fp.sqrt", this.exprFpUnaryOperator(FpSqrtExpr::create)); + this.addFunc("fp.max", this.exprBinaryOperator(FpMaxExpr::create)); + this.addFunc("fp.min", this.exprBinaryOperator(FpMinExpr::create)); + this.addFunc("++", this.exprMultiaryOperator(BvConcatExpr::create)); + this.addFunc("concat", this.exprMultiaryOperator(BvConcatExpr::create)); + this.addFunc("bvadd", this.exprMultiaryOperator(BvAddExpr::create)); + this.addFunc("bvsub", this.exprBinaryOperator(BvSubExpr::create)); + this.addFunc("bvpos", this.exprUnaryOperator(BvPosExpr::create)); + this.addFunc("bvneg", this.exprUnaryOperator(BvNegExpr::create)); + this.addFunc("bvmul", this.exprMultiaryOperator(BvMulExpr::create)); + this.addFunc("bvudiv", this.exprBinaryOperator(BvUDivExpr::create)); + this.addFunc("bvsdiv", this.exprBinaryOperator(BvSDivExpr::create)); + this.addFunc("bvsmod", this.exprBinaryOperator(BvSModExpr::create)); + this.addFunc("bvurem", this.exprBinaryOperator(BvURemExpr::create)); + this.addFunc("bvsrem", this.exprBinaryOperator(BvSRemExpr::create)); + this.addFunc("bvor", this.exprMultiaryOperator(BvOrExpr::create)); + this.addFunc("bvand", this.exprMultiaryOperator(BvAndExpr::create)); + this.addFunc("bvxor", this.exprMultiaryOperator(BvXorExpr::create)); + this.addFunc("bvnot", this.exprUnaryOperator(BvNotExpr::create)); + this.addFunc("bvshl", this.exprBinaryOperator(BvShiftLeftExpr::create)); + this.addFunc("bvashr", this.exprBinaryOperator(BvArithShiftRightExpr::create)); + this.addFunc("bvlshr", this.exprBinaryOperator(BvLogicShiftRightExpr::create)); + this.addFunc("bvrol", this.exprBinaryOperator(BvRotateLeftExpr::create)); + this.addFunc("ext_rotate_left", this.exprBinaryOperator(BvRotateLeftExpr::create)); + this.addFunc("bvror", this.exprBinaryOperator(BvRotateRightExpr::create)); + this.addFunc("ext_rotate_right", this.exprBinaryOperator(BvRotateRightExpr::create)); + this.addFunc("bvult", this.exprBinaryOperator(BvULtExpr::create)); + this.addFunc("bvule", this.exprBinaryOperator(BvULeqExpr::create)); + this.addFunc("bvugt", this.exprBinaryOperator(BvUGtExpr::create)); + this.addFunc("bvuge", this.exprBinaryOperator(BvUGeqExpr::create)); + this.addFunc("bvslt", this.exprBinaryOperator(BvSLtExpr::create)); + this.addFunc("bvsle", this.exprBinaryOperator(BvSLeqExpr::create)); + this.addFunc("bvsgt", this.exprBinaryOperator(BvSGtExpr::create)); + this.addFunc("bvsge", this.exprBinaryOperator(BvSGeqExpr::create)); + this.addFunc("read", this.exprBinaryOperator(ArrayReadExpr::create)); + this.addFunc("write", this.exprTernaryOperator(ArrayWriteExpr::create)); + this.addFunc("select", this.exprBinaryOperator(ArrayReadExpr::create)); + this.addFunc("store", this.exprTernaryOperator(ArrayWriteExpr::create)); + this.environment.put(Tuple2.of("fp.frombv", 1), (term, model, vars) -> { + FpType type = (FpType) transformSort(term.getSort()); + FpRoundingMode roundingmode = this.getRoundingMode((term.getArgs()[0]).toString()); + Expr op = (Expr) this.transform(term.getArgs()[1], model, vars); + return FpFromBvExpr.of(roundingmode, op, type, true); + }); + this.environment.put(Tuple2.of("fp.to_sbv", 2), (term, model, vars) -> { + BvType type = (BvType) transformSort(term.getSort()); + FpRoundingMode roundingmode = this.getRoundingMode((term.getArgs()[0]).toString()); + Expr op = (Expr) this.transform(term.getArgs()[1], model, vars); + return FpToBvExpr.of(roundingmode, op, type.getSize(), true); + }); + this.environment.put(Tuple2.of("fp.to_ubv", 2), (term, model, vars) -> { + BvType type = (BvType) transformSort(term.getSort()); + FpRoundingMode roundingmode = this.getRoundingMode((term.getArgs()[0]).toString()); + Expr op = (Expr) this.transform(term.getArgs()[1], model, vars); + return FpToBvExpr.of(roundingmode, op, type.getSize(), false); + }); + this.environment.put(Tuple2.of("to_fp", 2), (term, model, vars) -> { + FpType type = (FpType) transformSort(term.getSort()); + FpRoundingMode roundingmode = this.getRoundingMode((term.getArgs()[0]).toString()); + Expr op = this.transform(term.getArgs()[1], model, vars); + if (op.getType() instanceof FpType) { + return FpToFpExpr.of(roundingmode, (Expr) op, type.getExponent(), type.getSignificand()); + } else if (op.getType() instanceof BvType) { + return FpFromBvExpr.of(roundingmode, (Expr) op, FpType.of(type.getExponent(), type.getSignificand()), false); + } else { + throw new Z3Exception("Unsupported:" + op.getType()); + } + }); + this.environment.put(Tuple2.of("to_fp", 1), (term, model, vars) -> { + FpType type = (FpType) transformSort(term.getSort()); + Expr op = (Expr) this.transform(term.getArgs()[0], model, vars); + return FpFromBvExpr.of(FpRoundingMode.getDefaultRoundingMode(), op, FpType.of(type.getExponent(), type.getSignificand()), true); + }); + this.environment.put(Tuple2.of("extract", 1), (term, model, vars) -> { + Pattern pattern = Pattern.compile("extract ([0-9]+) ([0-9]+)"); + String termStr = term.toString(); + Matcher match = pattern.matcher(termStr); + if (match.find()) { + int to = Integer.parseInt(match.group(1)) + 1; + int from = Integer.parseInt(match.group(2)); + Expr op = (Expr) this.transform(term.getArgs()[0], model, vars); + return BvExtractExpr.of(op, IntExprs.Int(from), IntExprs.Int(to)); + } else { + throw new Z3Exception("Not supported: " + term); + } + }); + this.environment.put(Tuple2.of("zero_extend", 1), (term, model, vars) -> { + BvType type = (BvType) transformSort(term.getSort()); + Expr op = (Expr) this.transform(term.getArgs()[0], model, vars); + return BvZExtExpr.of(op, BvType.of(type.getSize())); + }); + this.environment.put(Tuple2.of("sign_extend", 1), (term, model, vars) -> { + BvType type = (BvType) transformSort(term.getSort()); + Expr op = (Expr) this.transform(term.getArgs()[0], model, vars); + return BvSExtExpr.of(op, BvType.of(type.getSize())); + }); + this.environment.put(Tuple2.of("EqZero", 1), (term, model, vars) -> { + Expr op = this.transform(term.getArgs()[0], model, vars); + return AbstractExprs.Eq(op, TypeUtils.getDefaultValue(op.getType())); + }); + this.environment.put(Tuple2.of("fp", 3), (term, model, vars) -> { + Expr op1 = (Expr) this.transform(term.getArgs()[0], model, vars); + Expr op2 = (Expr) this.transform(term.getArgs()[1], model, vars); + Expr op3 = (Expr) this.transform(term.getArgs()[2], model, vars); + return FpLitExpr.of(((BvLitExpr) op1).getValue()[0], (BvLitExpr) op2, (BvLitExpr) op3); + }); + this.environment.put(Tuple2.of("const", 1), (term, model, vars) -> { + return this.transform(term.getArgs()[0], model, vars); + }); + } + + private void addFunc(String name, Tuple2>, Expr>> func) { + checkArgument(!environment.containsKey(Tuple2.of(name, func.get1())), "Duplicate key: " + Tuple2.of(name, func.get1())); + environment.put(Tuple2.of(name, func.get1()), func.get2()); } + public Expr toExpr(final com.microsoft.z3legacy.Expr term) { return transform(term, null, new ArrayList<>()); } @@ -277,8 +483,12 @@ private Expr transformApp(final com.microsoft.z3legacy.Expr term, final Model final FuncDecl funcDecl = term.getFuncDecl(); final String symbol = funcDecl.getName().toString(); - if (environment.containsKey(symbol)) { - return environment.get(symbol).apply(term, model, vars); + final var key1 = Tuple2.of(symbol, term.getArgs().length); + final var key2 = Tuple2.of(symbol, -1); + if (environment.containsKey(key1)) { + return environment.get(key1).apply(term, model, vars); + } else if (environment.containsKey(key2)) { + return environment.get(key2).apply(term, model, vars); } else { final Expr funcExpr; if (symbolTable.definesSymbol(funcDecl)) { @@ -470,55 +680,126 @@ private Expr transformUnsupported(final com.microsoft.z3legacy.Expr term, fin //// - private TriFunction>, Expr> exprNullaryOperator( + + private Tuple2>, Expr>> exprFpUnaryOperator( + final BiFunction, Expr> function) { + return Tuple2.of(2, (term, model, vars) -> { + checkArgument(term.getArgs().length == 2, "Number of arguments must be two"); + final var roundingmode = getRoundingMode(term.getArgs()[0].toString()); + final Expr op2 = transform(term.getArgs()[1], model, vars); + return function.apply(roundingmode, op2); + }); + } + + private Tuple2>, Expr>> exprFpBinaryOperator( + final TriFunction, Expr, Expr> function) { + return Tuple2.of(3, (term, model, vars) -> { + checkArgument(term.getArgs().length == 3, "Number of arguments must be three"); + final var roundingmode = getRoundingMode(term.getArgs()[0].toString()); + final Expr op1 = transform(term.getArgs()[1], model, vars); + final Expr op2 = transform(term.getArgs()[2], model, vars); + return function.apply(roundingmode, op1, op2); + }); + } + + private Tuple2>, Expr>> exprFpMultiaryOperator( + final BiFunction>, Expr> function) { + return Tuple2.of(-1, (term, model, vars) -> { + final var roundingmode = getRoundingMode(term.getArgs()[0].toString()); + final List> ops = Arrays.stream(term.getArgs()).skip(1).map(arg -> transform(arg, model, vars)) + .collect(toImmutableList()); + return function.apply(roundingmode, ops); + }); + } + + private Tuple2>, Expr>> exprFpLitUnaryOperator( + final BiFunction> function) { + return Tuple2.of(3, (term, model, vars) -> { + final BvLitExpr op1 = (BvLitExpr) transform(term.getArgs()[0], model, vars); + final IntLitExpr op2 = (IntLitExpr) transform(term.getArgs()[1], model, vars); + final IntLitExpr op3 = (IntLitExpr) transform(term.getArgs()[2], model, vars); + return function.apply(op1, FpType.of(op2.getValue().intValue(), op3.getValue().intValue() + 1)); + }); + } + + private Tuple2>, Expr>> exprNullaryOperator( final Supplier> function) { - return (term, model, vars) -> { + return Tuple2.of(0, (term, model, vars) -> { final com.microsoft.z3legacy.Expr[] args = term.getArgs(); checkArgument(args.length == 0, "Number of arguments must be zero"); return function.get(); - }; + }); } - private TriFunction>, Expr> exprUnaryOperator( + private Tuple2>, Expr>> exprUnaryOperator( final UnaryOperator> function) { - return (term, model, vars) -> { + return Tuple2.of(1, (term, model, vars) -> { final com.microsoft.z3legacy.Expr[] args = term.getArgs(); checkArgument(args.length == 1, "Number of arguments must be one"); final Expr op = transform(args[0], model, vars); return function.apply(op); - }; + }); } - private TriFunction>, Expr> exprBinaryOperator( + private Tuple2>, Expr>> exprBinaryOperator( final BinaryOperator> function) { - return (term, model, vars) -> { + return Tuple2.of(2, (term, model, vars) -> { final com.microsoft.z3legacy.Expr[] args = term.getArgs(); checkArgument(args.length == 2, "Number of arguments must be two"); final Expr op1 = transform(args[0], model, vars); final Expr op2 = transform(args[1], model, vars); return function.apply(op1, op2); - }; + }); } - private TriFunction>, Expr> exprTernaryOperator( + private Tuple2>, Expr>> reference() { + return Tuple2.of(1, (term, model, vars) -> { + final com.microsoft.z3legacy.Expr[] args = term.getArgs(); + checkArgument(args.length == 1, "Number of arguments must be one"); + final Expr op = transform(args[0], model, vars); + return Exprs.Reference(op, transformSort(term.getSort())); + }); + } + + private Tuple2>, Expr>> dereference() { + return Tuple2.of(3, (term, model, vars) -> { + final com.microsoft.z3legacy.Expr[] args = term.getArgs(); + checkArgument(args.length == 3, "Number of arguments must be three"); + final Expr op1 = (Expr) transform(args[0], model, vars); + final Expr op2 = (Expr) transform(args[1], model, vars); + final Expr op3 = (Expr) transform(args[2], model, vars); + return Exprs.Dereference(op1, op2, transformSort(term.getSort())).withUniquenessExpr(op3); + }); + } + + private Tuple2>, Expr>> exprTernaryOperator( final TernaryOperator> function) { - return (term, model, vars) -> { + return Tuple2.of(3, (term, model, vars) -> { final com.microsoft.z3legacy.Expr[] args = term.getArgs(); checkArgument(args.length == 3, "Number of arguments must be three"); final Expr op1 = transform(args[0], model, vars); final Expr op2 = transform(args[1], model, vars); final Expr op3 = transform(args[2], model, vars); return function.apply(op1, op2, op3); - }; + }); } - private TriFunction>, Expr> exprMultiaryOperator( + private Tuple2>, Expr>> exprMultiaryOperator( final Function>, Expr> function) { - return (term, model, vars) -> { + return Tuple2.of(-1, (term, model, vars) -> { final com.microsoft.z3legacy.Expr[] args = term.getArgs(); final List> ops = Stream.of(args).map(arg -> transform(arg, model, vars)) .collect(toImmutableList()); return function.apply(ops); + }); + } + + private FpRoundingMode getRoundingMode(String s) { + return switch (s) { + case "roundNearestTiesToAway" -> FpRoundingMode.RNA; + case "roundNearestTiesToEven" -> FpRoundingMode.RNE; + case "roundTowardZero" -> FpRoundingMode.RTZ; + default -> throw new Z3Exception("Unexpected value: " + s); }; } 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 a5e8ff2ec8..6dccaaa36c 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 @@ -19,24 +19,27 @@ package hu.bme.mit.theta.c2xcfa import com.google.common.base.Preconditions import hu.bme.mit.theta.common.logging.Logger -import hu.bme.mit.theta.common.logging.Logger.Level import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.AssignStmt +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt import hu.bme.mit.theta.core.stmt.Stmts import hu.bme.mit.theta.core.stmt.Stmts.Assume 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 +import hu.bme.mit.theta.core.type.anytype.Dereference +import hu.bme.mit.theta.core.type.anytype.Exprs.Dereference import hu.bme.mit.theta.core.type.anytype.RefExpr -import hu.bme.mit.theta.core.type.arraytype.* import hu.bme.mit.theta.core.type.booltype.BoolExprs 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.bvtype.BvLitExpr +import hu.bme.mit.theta.core.type.inttype.IntLitExpr +import hu.bme.mit.theta.core.utils.BvUtils +import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.frontend.ParseContext -import hu.bme.mit.theta.frontend.transformation.grammar.expression.Dereference -import hu.bme.mit.theta.frontend.transformation.grammar.expression.Reference +import hu.bme.mit.theta.frontend.transformation.grammar.expression.UnsupportedInitializer import hu.bme.mit.theta.frontend.transformation.model.statements.* import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType import hu.bme.mit.theta.frontend.transformation.model.types.complex.CVoid @@ -47,16 +50,18 @@ import hu.bme.mit.theta.frontend.transformation.model.types.complex.integer.CInt import hu.bme.mit.theta.frontend.transformation.model.types.simple.CSimpleTypeFactory import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.CPasses -import java.util.* -import java.util.Set +import org.abego.treelayout.internal.util.Contract.checkState +import java.math.BigInteger import java.util.stream.Collectors -import kotlin.collections.set class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boolean = false, val uniqueWarningLogger: Logger) : CStatementVisitorBase() { private val locationLut: MutableMap = LinkedHashMap() + private var ptrCnt = 1 // counts up, uses 3k+1 + get() = field.also { field += 3 } + private fun getLoc(builder: XcfaProcedureBuilder, name: String?, metadata: MetaData): XcfaLocation { if (name == null) return getAnonymousLoc(builder, metadata = metadata) @@ -85,25 +90,60 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo val initStmtList: MutableList = ArrayList() for (globalDeclaration in cProgram.globalDeclarations) { val type = CComplexType.getType(globalDeclaration.get2().ref, parseContext) - if (type is CVoid || type is CStruct) { - uniqueWarningLogger.write(Level.INFO, - "WARNING: Not handling init expression of " + globalDeclaration.get1() + " as it is non initializable\n") + if (type is CVoid) { continue } + if (type is CStruct) { + error("Not handling init expression of struct array ${globalDeclaration.get1()}") + } builder.addVar(XcfaGlobalVar(globalDeclaration.get2(), type.nullValue)) - if (globalDeclaration.get1().initExpr != null) { + if (type is CArray) { initStmtList.add(StmtLabel( Stmts.Assign(cast(globalDeclaration.get2(), globalDeclaration.get2().type), - cast(type.castTo(globalDeclaration.get1().initExpr.expression), - globalDeclaration.get2().type)), - metadata = EmptyMetaData + cast(type.getValue("$ptrCnt"), globalDeclaration.get2().type)) )) } else { - initStmtList.add(StmtLabel( - Stmts.Assign(cast(globalDeclaration.get2(), globalDeclaration.get2().type), - cast(type.nullValue, globalDeclaration.get2().type)), - metadata = EmptyMetaData - )) + if (globalDeclaration.get1().initExpr != null && globalDeclaration.get1().initExpr.expression !is UnsupportedInitializer) { + initStmtList.add(StmtLabel( + Stmts.Assign(cast(globalDeclaration.get2(), globalDeclaration.get2().type), + cast(type.castTo(globalDeclaration.get1().initExpr.expression), + globalDeclaration.get2().type)) + )) + } else { + initStmtList.add(StmtLabel( + Stmts.Assign(cast(globalDeclaration.get2(), globalDeclaration.get2().type), + cast(type.nullValue, globalDeclaration.get2().type)) + )) + } + } + + if (globalDeclaration.get1().arrayDimensions.size == 1) { + val bounds = ExprUtils.simplify(CComplexType.getUnsignedLong(parseContext) + .castTo(globalDeclaration.get1().arrayDimensions[0].expression)) + checkState(bounds is IntLitExpr || bounds is BvLitExpr, + "Only IntLit and BvLit expression expected here.") + val literalValue = if (bounds is IntLitExpr) bounds.value.toLong() else BvUtils.neutralBvLitExprToBigInteger( + bounds as BvLitExpr).toLong() + val literalToExpr = { x: Long -> + if (bounds is IntLitExpr) IntLitExpr.of( + BigInteger.valueOf(x)) else BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.valueOf(x), + (bounds as BvLitExpr).type.size) + } + val initExprs: Map> = (globalDeclaration.get1()?.initExpr as? CInitializerList)?.statements?.mapIndexed { i, it -> + Pair(i, it.get2().expression) + }?.toMap() ?: emptyMap() + for (i in 0 until literalValue) { + checkState(globalDeclaration.get1().actualType is CArray, "Only arrays are expected here") + val embeddedType = (globalDeclaration.get1().actualType as CArray).embeddedType + initStmtList.add(StmtLabel( + Stmts.MemoryAssign( + Dereference(globalDeclaration.get2().ref, literalToExpr(i), embeddedType.smtType), + cast(initExprs[i.toInt()]?.let { embeddedType.castTo(it) } ?: embeddedType.nullValue, + embeddedType.smtType)) + )) + } + } else if (globalDeclaration.get1().arrayDimensions.size > 1) { + error("Not handling init expression of high dimsension array ${globalDeclaration.get1()}") } } for (function in cProgram.functions) { @@ -121,8 +161,9 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo val compound = function.compound val builder = XcfaProcedureBuilder(funcDecl.name, CPasses(checkOverflow, parseContext, uniqueWarningLogger)) xcfaBuilder.addProcedure(builder) - for (flatVariable in flatVariables) { - builder.addVar(flatVariable) + val initStmtList = ArrayList() + if (param.size > 0 && builder.name.equals("main")) { + initStmtList.addAll(param) } // builder.setRetType(if (funcDecl.actualType is CVoid) null else funcDecl.actualType.smtType) TODO: we never need the ret type, do we? if (funcDecl.actualType !is CVoid) { @@ -145,16 +186,34 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo if (varDecl != null) builder.addParam(varDecl, ParamDirection.IN) } } + + for (flatVariable in flatVariables) { + builder.addVar(flatVariable) + val type = CComplexType.getType(flatVariable.ref, parseContext) + if (type is CArray && builder.getParams().none { it.first == flatVariable }) { + initStmtList.add(StmtLabel( + Stmts.Assign(cast(flatVariable, flatVariable.type), + cast(type.getValue("$ptrCnt"), flatVariable.type)) + )) + } + } builder.createInitLoc(getMetadata(function)) var init = builder.initLoc - if (param.size > 0 && builder.name.equals("main")) { - val endinit = getAnonymousLoc(builder, getMetadata(function)) - builder.addLoc(endinit) - val edge = XcfaEdge(init, endinit, SequenceLabel(param), - metadata = getMetadata(function)) - builder.addEdge(edge) - init = endinit + + for (flatVariable in flatVariables) { + val type = CComplexType.getType(flatVariable.ref, parseContext) + if (type is CArray && type.embeddedType is CArray) { + // some day this is where initialization will occur. But this is not today. + error("Not handling init expression of high dimsension array $flatVariable") + } } + + val endinit = getAnonymousLoc(builder, getMetadata(function)) + builder.addLoc(endinit) + val initEdge = XcfaEdge(init, endinit, SequenceLabel(initStmtList), + metadata = getMetadata(function)) + builder.addEdge(initEdge) + init = endinit builder.createFinalLoc(getMetadata(function)) val ret = builder.finalLoc.get() builder.addLoc(ret) @@ -172,7 +231,6 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo val returnLoc = param.returnLoc val lValue = statement.getlValue() val rValue = statement.getrValue() - val memoryMaps = CAssignment.getMemoryMaps() var initLoc = getLoc(builder, statement.id, metadata = getMetadata(statement)) builder.addLoc(initLoc) var xcfaEdge = XcfaEdge(lastLoc, initLoc, metadata = getMetadata(statement)) @@ -180,81 +238,42 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo val location = getAnonymousLoc(builder, metadata = getMetadata(statement)) builder.addLoc(location) initLoc = rValue.accept(this, ParamPack(builder, initLoc, breakLoc, continueLoc, returnLoc)) - Preconditions.checkState( - lValue is Dereference<*, *> || lValue is ArrayReadExpr<*, *> || lValue is RefExpr<*> && lValue.decl is VarDecl<*>, - "lValue must be a variable, pointer dereference or an array element!") val rExpression = statement.getrExpression() - val label: StmtLabel = if (lValue is ArrayReadExpr<*, *>) { - val exprs = Stack>() - val toAdd = createArrayWriteExpr(lValue as ArrayReadExpr<*, out Type>, rExpression, - exprs) - StmtLabel(Stmts.Assign(cast(toAdd, toAdd.type), cast(exprs.pop(), toAdd.type)), - metadata = getMetadata(statement)) - } else if (lValue is Dereference<*, *>) { - val op = lValue.op - val type = op.type - val ptrType = CComplexType.getUnsignedLong(parseContext).smtType - if (!memoryMaps.containsKey(type)) { - val toAdd = Decls.Var>("memory_$type", ArrayType.of(ptrType, type)) - builder.parent.addVar(XcfaGlobalVar(toAdd, - ArrayLitExpr.of(emptyList(), cast(CComplexType.getType(op, parseContext).nullValue, type), - ArrayType.of(ptrType, type)))) - memoryMaps[type] = toAdd - parseContext.metadata.create(toAdd, "defaultElement", CComplexType.getType(op, parseContext).nullValue) + val label: StmtLabel = when (lValue) { + is Dereference<*, *, *> -> { + val op = cast(lValue.array, lValue.array.type) + val offset = cast(lValue.offset, op.type) + val castRExpression = CComplexType.getType(lValue, parseContext).castTo(rExpression) + val type = CComplexType.getType(castRExpression, parseContext) + + val deref = Dereference(op, offset, type.smtType) + + val memassign = MemoryAssignStmt.create(deref, castRExpression) + + parseContext.metadata.create(deref, "cType", CPointer(null, type, parseContext)) + StmtLabel(memassign, metadata = getMetadata(statement)) } - val memoryMap = checkNotNull(memoryMaps[type]) - parseContext.metadata.create(op, "dereferenced", true) - parseContext.metadata.create(op, "refSubstitute", memoryMap) - val write = ArrayExprs.Write(cast(memoryMap.ref, ArrayType.of(ptrType, type)), - cast(lValue.op, ptrType), - cast(rExpression, type)) - parseContext.metadata.create(write, "cType", - CArray(null, CComplexType.getType(lValue.op, parseContext), parseContext)) - StmtLabel(Stmts.Assign(cast(memoryMap, ArrayType.of(ptrType, type)), write), - metadata = getMetadata(statement)) - } else { - val label = StmtLabel(Stmts.Assign( - cast((lValue as RefExpr<*>).decl as VarDecl<*>, (lValue.decl as VarDecl<*>).type), - cast(CComplexType.getType(lValue, parseContext).castTo(rExpression), lValue.type)), - metadata = getMetadata(statement)) - if (CComplexType.getType(lValue, parseContext) is CPointer && CComplexType.getType( - rExpression, parseContext) is CPointer) { - Preconditions.checkState( - rExpression is RefExpr<*> || rExpression is Reference<*, *>) - if (rExpression is RefExpr<*>) { - var pointsTo = parseContext.metadata.getMetadataValue(lValue, "pointsTo") - if (pointsTo.isPresent && pointsTo.get() is Collection<*>) { - (pointsTo.get() as MutableCollection?>).add(rExpression) - } else { - pointsTo = Optional.of(LinkedHashSet>(Set.of(rExpression))) - } - parseContext.metadata.create(lValue, "pointsTo", pointsTo.get()) - } else { - var pointsTo = parseContext.metadata.getMetadataValue(lValue, "pointsTo") - if (pointsTo.isPresent && pointsTo.get() is Collection<*>) { - (pointsTo.get() as MutableCollection?>).add( - (rExpression as Reference<*, *>).op) - } else { - pointsTo = Optional.of( - LinkedHashSet(Set.of((rExpression as Reference<*, *>).op))) - } - parseContext.metadata.create(lValue, "pointsTo", pointsTo.get()) - } + + is RefExpr<*> -> { + StmtLabel(Stmts.Assign( + cast(lValue.decl as VarDecl<*>, (lValue.decl as VarDecl<*>).type), + cast(CComplexType.getType(lValue, parseContext).castTo(rExpression), lValue.type)), + metadata = getMetadata(statement)) } - label - } - val lhs = (label.stmt as AssignStmt<*>).varDecl - val type: CComplexType? = try { - CComplexType.getType(lhs.ref, parseContext) - } catch (_: Exception) { - null + else -> { + error("Could not handle left-hand side of assignment $statement") + } } + val lhs = (label.stmt as? AssignStmt<*>)?.varDecl + val type = lhs?.let { CComplexType.getType(it.ref, parseContext) } + if (!checkOverflow || type == null || type !is CInteger || !type.isSsigned) { xcfaEdge = XcfaEdge(initLoc, location, label, metadata = getMetadata(statement)) builder.addEdge(xcfaEdge) } else { + lhs!! val middleLoc1 = getAnonymousLoc(builder, getMetadata(statement)) val middleLoc2 = getAnonymousLoc(builder, getMetadata(statement)) xcfaEdge = XcfaEdge(initLoc, middleLoc1, label, metadata = getMetadata(statement)) @@ -276,23 +295,6 @@ class FrontendXcfaBuilder(val parseContext: ParseContext, val checkOverflow: Boo return location } - private fun

createArrayWriteExpr(lValue: ArrayReadExpr, - rExpression: Expr<*>, exprs: Stack>): VarDecl<*> { - val array = lValue.array - val index = lValue.index - val arrType = CComplexType.getType(array, parseContext) - check(arrType is CArray) - val castExpr = arrType.embeddedType.castTo(rExpression) - val arrayWriteExpr = ArrayWriteExpr.of(array, index, cast(castExpr, array.type.elemType)) - parseContext.metadata.create(arrayWriteExpr, "cType", arrType) - return if (array is RefExpr<*> && (array as RefExpr>).decl is VarDecl<*>) { - exprs.push(arrayWriteExpr) - array.decl as VarDecl<*> - } else if (array is ArrayReadExpr<*, *>) { - createArrayWriteExpr(array as ArrayReadExpr, arrayWriteExpr, exprs) - } else throw UnsupportedOperationException("Possible malformed array write?") - } - override fun visit(statement: CAssume, param: ParamPack): XcfaLocation { val builder: XcfaProcedureBuilder = param.builder val lastLoc = param.lastLoc diff --git a/subprojects/xcfa/c2xcfa/src/test/resources/12ptrtypes.c b/subprojects/xcfa/c2xcfa/src/test/resources/12ptrtypes.c index f42a220292..aa785ff812 100644 --- a/subprojects/xcfa/c2xcfa/src/test/resources/12ptrtypes.c +++ b/subprojects/xcfa/c2xcfa/src/test/resources/12ptrtypes.c @@ -1,6 +1,6 @@ void reach_error(){} -void check_geq_110(void* param) { +void check_geq_110(unsigned int* param) { if(*(unsigned int*)param <= 110) reach_error(); } diff --git a/subprojects/xcfa/c2xcfa/src/test/resources/18multithread.c b/subprojects/xcfa/c2xcfa/src/test/resources/18multithread.c index 8ca102134e..69f3c35b8d 100644 --- a/subprojects/xcfa/c2xcfa/src/test/resources/18multithread.c +++ b/subprojects/xcfa/c2xcfa/src/test/resources/18multithread.c @@ -686,6 +686,9 @@ extern int pthread_atfork (void (*__prepare) (void), void reach_error(){} +void __VERIFIER_atomic_begin() {} +void __VERIFIER_atomic_end() {} + int x = 0; int ERR = 0; diff --git a/subprojects/xcfa/c2xcfa/src/test/resources/21namecollision.c b/subprojects/xcfa/c2xcfa/src/test/resources/21namecollision.c index f4df8686d0..6011759feb 100644 --- a/subprojects/xcfa/c2xcfa/src/test/resources/21namecollision.c +++ b/subprojects/xcfa/c2xcfa/src/test/resources/21namecollision.c @@ -1,3 +1,4 @@ +void reach_error(){} int f(int x) { return x - 1; } diff --git a/subprojects/xcfa/c2xcfa/src/test/resources/22nondet.c b/subprojects/xcfa/c2xcfa/src/test/resources/22nondet.c index 66461be532..b17fdd1a9d 100644 --- a/subprojects/xcfa/c2xcfa/src/test/resources/22nondet.c +++ b/subprojects/xcfa/c2xcfa/src/test/resources/22nondet.c @@ -1,3 +1,4 @@ +void reach_error(){} extern int __VERIFIER_nondet_int(); int main() { int i = 0; diff --git a/subprojects/xcfa/c2xcfa/src/test/resources/23exotic.c b/subprojects/xcfa/c2xcfa/src/test/resources/23exotic.c index 57c0ff3afb..150402fcc0 100644 --- a/subprojects/xcfa/c2xcfa/src/test/resources/23exotic.c +++ b/subprojects/xcfa/c2xcfa/src/test/resources/23exotic.c @@ -1,3 +1,4 @@ +void reach_error(){} extern unsigned int __VERIFIER_nondet_uint(); int main() { int i = 0; diff --git a/subprojects/xcfa/litmus2xcfa/src/main/java/hu/bme/mit/theta/frontend/litmus2xcfa/dsl/LitmusAArch64.java b/subprojects/xcfa/litmus2xcfa/src/main/java/hu/bme/mit/theta/frontend/litmus2xcfa/dsl/LitmusAArch64.java index 57bb1958f1..a6e01016db 100644 --- a/subprojects/xcfa/litmus2xcfa/src/main/java/hu/bme/mit/theta/frontend/litmus2xcfa/dsl/LitmusAArch64.java +++ b/subprojects/xcfa/litmus2xcfa/src/main/java/hu/bme/mit/theta/frontend/litmus2xcfa/dsl/LitmusAArch64.java @@ -179,22 +179,22 @@ public Expr visitRegister32(LitmusAArch64Parser.Register32Context ctx) { private class LabelVisitor extends LitmusAArch64BaseVisitor { @Override public XcfaLabel visitMov32(LitmusAArch64Parser.Mov32Context ctx) { - return new StmtLabel(Assign(getOrCreateVar(ctx.r32.getText(), 32), ctx.expr32().accept(expressionVisitor)), EmptyMetaData.INSTANCE); + return new StmtLabel(Assign(getOrCreateVar(ctx.r32.getText(), 32), ctx.expr32().accept(expressionVisitor))); } @Override public XcfaLabel visitMov64(LitmusAArch64Parser.Mov64Context ctx) { - return new StmtLabel(Assign(getOrCreateVar(ctx.r64.getText(), 64), ctx.expr64().accept(expressionVisitor)), EmptyMetaData.INSTANCE); + return new StmtLabel(Assign(getOrCreateVar(ctx.r64.getText(), 64), ctx.expr64().accept(expressionVisitor))); } @Override public XcfaLabel visitCmp32(LitmusAArch64Parser.Cmp32Context ctx) { - return new StmtLabel(Assign(getOrCreateVar("Xcmp", 32), Sub(ctx.r32.accept(expressionVisitor), ctx.expr32().accept(expressionVisitor))), EmptyMetaData.INSTANCE); + return new StmtLabel(Assign(getOrCreateVar("Xcmp", 32), Sub(ctx.r32.accept(expressionVisitor), ctx.expr32().accept(expressionVisitor)))); } @Override public XcfaLabel visitCmp64(LitmusAArch64Parser.Cmp64Context ctx) { - return new StmtLabel(Assign(getOrCreateVar("Wcmp", 64), Sub(ctx.r64.accept(expressionVisitor), ctx.expr64().accept(expressionVisitor))), EmptyMetaData.INSTANCE); + return new StmtLabel(Assign(getOrCreateVar("Wcmp", 64), Sub(ctx.r64.accept(expressionVisitor), ctx.expr64().accept(expressionVisitor)))); } @Override @@ -203,13 +203,10 @@ public XcfaLabel visitArith32(LitmusAArch64Parser.Arith32Context ctx) { final Expr a = ctx.rV32.accept(expressionVisitor); final Expr b = ctx.expr32().accept(expressionVisitor); return switch (ctx.arithmeticInstruction().getText()) { - case "ADD" -> - new StmtLabel(Assign(target, Add(List.of(a, b))), EmptyMetaData.INSTANCE); - case "SUB" -> new StmtLabel(Assign(target, Sub(a, b)), EmptyMetaData.INSTANCE); - case "EOR" -> - new StmtLabel(Assign(target, Xor(List.of(a, b))), EmptyMetaData.INSTANCE); - case "AND" -> - new StmtLabel(Assign(target, And(List.of(a, b))), EmptyMetaData.INSTANCE); + case "ADD" -> new StmtLabel(Assign(target, Add(List.of(a, b)))); + case "SUB" -> new StmtLabel(Assign(target, Sub(a, b))); + case "EOR" -> new StmtLabel(Assign(target, Xor(List.of(a, b)))); + case "AND" -> new StmtLabel(Assign(target, And(List.of(a, b)))); default -> throw new UnsupportedOperationException("Arithmetic instruction " + ctx.arithmeticInstruction().getText() + " not supported."); }; @@ -221,13 +218,10 @@ public XcfaLabel visitArith64(LitmusAArch64Parser.Arith64Context ctx) { final Expr a = ctx.rV64.accept(expressionVisitor); final Expr b = ctx.expr64().accept(expressionVisitor); return switch (ctx.arithmeticInstruction().getText()) { - case "ADD" -> - new StmtLabel(Assign(target, Add(List.of(a, b))), EmptyMetaData.INSTANCE); - case "SUB" -> new StmtLabel(Assign(target, Sub(a, b)), EmptyMetaData.INSTANCE); - case "EOR" -> - new StmtLabel(Assign(target, Xor(List.of(a, b))), EmptyMetaData.INSTANCE); - case "AND" -> - new StmtLabel(Assign(target, And(List.of(a, b))), EmptyMetaData.INSTANCE); + case "ADD" -> new StmtLabel(Assign(target, Add(List.of(a, b)))); + case "SUB" -> new StmtLabel(Assign(target, Sub(a, b))); + case "EOR" -> new StmtLabel(Assign(target, Xor(List.of(a, b)))); + case "AND" -> new StmtLabel(Assign(target, And(List.of(a, b)))); default -> throw new UnsupportedOperationException("Arithmetic instruction " + ctx.arithmeticInstruction().getText() + " not supported."); }; @@ -237,7 +231,7 @@ public XcfaLabel visitArith64(LitmusAArch64Parser.Arith64Context ctx) { public XcfaLabel visitLoad32(LitmusAArch64Parser.Load32Context ctx) { final VarDecl var = getOrCreateVar(ctx.rD32.getText(), 32); final VarDecl tmp = getOrCreateVar("tmp" + XcfaLocation.Companion.uniqueCounter(), 64); - StmtLabel cast = new StmtLabel(Assign(var, Extract(tmp.getRef(), Int(0), Int(32))), EmptyMetaData.INSTANCE); + StmtLabel cast = new StmtLabel(Assign(var, Extract(tmp.getRef(), Int(0), Int(32)))); ReadLabel load = new ReadLabel(getGlobalFromReg(ctx.address().r.getText()), tmp, Set.of(ctx.loadInstruction().mo), EmptyMetaData.INSTANCE); return new SequenceLabel(List.of(load, cast)); } @@ -251,7 +245,7 @@ public XcfaLabel visitLoad64(LitmusAArch64Parser.Load64Context ctx) { public XcfaLabel visitStore32(LitmusAArch64Parser.Store32Context ctx) { final VarDecl var = getOrCreateVar(ctx.rV32.getText(), 32); final VarDecl tmp = getOrCreateVar("tmp" + XcfaLocation.Companion.uniqueCounter(), 64); - StmtLabel cast = new StmtLabel(Assign(tmp, ZExt(var.getRef(), BvType(64))), EmptyMetaData.INSTANCE); + StmtLabel cast = new StmtLabel(Assign(tmp, ZExt(var.getRef(), BvType(64)))); WriteLabel store = new WriteLabel(getGlobalFromReg(ctx.address().r.getText()), tmp, Set.of(ctx.storeInstruction().mo), EmptyMetaData.INSTANCE); return new SequenceLabel(List.of(cast, store)); } @@ -273,11 +267,11 @@ public XcfaLocation visitBranchRegister32(LitmusAArch64Parser.BranchRegister32Co VarDecl var = getOrCreateVar(ctx.rV32.getText(), 32); final StmtLabel stmt1, stmt2; if (ctx.branchRegInstruction().zerotest) { - stmt1 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32))), EmptyMetaData.INSTANCE); - stmt2 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32))), EmptyMetaData.INSTANCE); + stmt1 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32)))); + stmt2 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32)))); } else { - stmt1 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32))), EmptyMetaData.INSTANCE); - stmt2 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32))), EmptyMetaData.INSTANCE); + stmt1 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32)))); + stmt2 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 32)))); } final XcfaLocation branchTo = getOrCreateLoc(ctx.label().getText()); final XcfaLocation newLoc = newAnonymousLoc(); @@ -298,11 +292,11 @@ public XcfaLocation visitBranchRegister64(LitmusAArch64Parser.BranchRegister64Co VarDecl var = getOrCreateVar(ctx.rV64.getText(), 64); final StmtLabel stmt1, stmt2; if (ctx.branchRegInstruction().zerotest) { - stmt1 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64))), EmptyMetaData.INSTANCE); - stmt2 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64))), EmptyMetaData.INSTANCE); + stmt1 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64)))); + stmt2 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64)))); } else { - stmt1 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64))), EmptyMetaData.INSTANCE); - stmt2 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64))), EmptyMetaData.INSTANCE); + stmt1 = new StmtLabel(Assume(Neq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64)))); + stmt2 = new StmtLabel(Assume(Eq(var.getRef(), BvUtils.bigIntegerToNeutralBvLitExpr(BigInteger.ZERO, 64)))); } final XcfaLocation branchTo = getOrCreateLoc(ctx.label().getText()); final XcfaLocation newLoc = newAnonymousLoc(); diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/Utils.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/Utils.java index 424396114c..010d621de5 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/Utils.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/Utils.java @@ -187,7 +187,7 @@ public static void foldExpression(Instruction instruction, FunctionState functio Stmt stmt = Assign(cast(lhs, lhs.getType()), cast(op, lhs.getType())); XcfaEdge edge; if (!lhs.getRef().equals(op)) - edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt, EmptyMetaData.INSTANCE), new LlvmMetadata(instruction.getLineNumber())); + edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt), new LlvmMetadata(instruction.getLineNumber())); else edge = new XcfaEdge(blockState.getLastLocation(), loc, NopLabel.INSTANCE, new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addLoc(loc); diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/ArrayIntrinsicsHandler.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/ArrayIntrinsicsHandler.java index a3f84af3b9..8f6bac344e 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/ArrayIntrinsicsHandler.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/ArrayIntrinsicsHandler.java @@ -82,7 +82,7 @@ private void setArrayElement(Instruction instruction, GlobalState globalState, F Expr expr = ArrayExprs.Write(cast(var.getRef(), ArrayType.of(Int(), val.getType())), cast(idx.getExpr(functionState.getValues()), Int()), cast(val.getExpr(functionState.getValues()), val.getType())); Stmt stmt = Assign(cast(var, var.getType()), cast(expr, var.getType())); - XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt, EmptyMetaData.INSTANCE), new LlvmMetadata(instruction.getLineNumber())); + XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt), new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addLoc(loc); functionState.getProcedureBuilder().addEdge(edge); blockState.setLastLocation(loc); diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/MemoryInstructionHandler.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/MemoryInstructionHandler.java index 372ee24371..45705421f7 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/MemoryInstructionHandler.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/MemoryInstructionHandler.java @@ -112,7 +112,7 @@ private void store(Instruction instruction, GlobalState globalState, FunctionSta XcfaLocation loc = new XcfaLocation(blockState.getName() + "_" + blockState.getBlockCnt()); VarDecl var = functionState.getLocalVars().get(op2.getName()).get1(); Stmt stmt = Assign(cast(var, var.getType()), cast(op1.getExpr(functionState.getValues()), var.getType())); - XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt, EmptyMetaData.INSTANCE), new LlvmMetadata(instruction.getLineNumber())); + XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(stmt), new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addLoc(loc); functionState.getProcedureBuilder().addEdge(edge); blockState.setLastLocation(loc); diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/OtherInstructionHandler.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/OtherInstructionHandler.java index 4dc62aabdd..422a3639d4 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/OtherInstructionHandler.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/OtherInstructionHandler.java @@ -108,7 +108,7 @@ private void call(Instruction instruction, GlobalState globalState, FunctionStat if (objects != null && objects.get2() > 0) stmts.add(havocVar(argument, functionState, blockState)); } - XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), newLoc, new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt, EmptyMetaData.INSTANCE)).toList()), new LlvmMetadata(instruction.getLineNumber())); + XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), newLoc, new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt)).toList()), new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addLoc(newLoc); functionState.getProcedureBuilder().addEdge(edge); } diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/TerminatorInstructionHandler.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/TerminatorInstructionHandler.java index e012cfca3d..f2ee090799 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/TerminatorInstructionHandler.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/concrete/TerminatorInstructionHandler.java @@ -102,7 +102,7 @@ private void ret(Instruction instruction, GlobalState globalState, FunctionState default: throw new IllegalStateException("Unexpected value: " + instruction.getArguments().size()); } - XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), functionState.getProcedureBuilder().getFinalLoc().orElseThrow(), new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt, EmptyMetaData.INSTANCE)).toList()), new LlvmMetadata(instruction.getLineNumber())); + XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), functionState.getProcedureBuilder().getFinalLoc().orElseThrow(), new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt)).toList()), new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addEdge(edge); blockState.setLastLocation(functionState.getProcedureBuilder().getFinalLoc().orElseThrow()); } @@ -156,7 +156,7 @@ private void sw(Instruction instruction, GlobalState globalState, FunctionState functionState.getInterBlockEdges().put(key, Tuple4.of(blockState.getLastLocation(), loc, stmts, instruction.getLineNumber())); } XcfaLocation loc = functionState.getLocations().get(instruction.getArguments().get(1).getName()); - XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(Assume(BoolExprs.Not(defaultBranch)), EmptyMetaData.INSTANCE), new LlvmMetadata(instruction.getLineNumber())); + XcfaEdge edge = new XcfaEdge(blockState.getLastLocation(), loc, new StmtLabel(Assume(BoolExprs.Not(defaultBranch))), new LlvmMetadata(instruction.getLineNumber())); functionState.getProcedureBuilder().addEdge(edge); blockState.setLastLocation(functionState.getProcedureBuilder().getFinalLoc().get()); } diff --git a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/states/FunctionState.java b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/states/FunctionState.java index 96eef76279..7a866cd44c 100644 --- a/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/states/FunctionState.java +++ b/subprojects/xcfa/llvm2xcfa/src/main/java/hu/bme/mit/theta/llvm2xcfa/handlers/states/FunctionState.java @@ -119,7 +119,7 @@ public void finalizeFunctionState(BuiltState builtState) { } return stmt; }).collect(Collectors.toUnmodifiableList()); - XcfaEdge edge = new XcfaEdge(edgeTup.get1(), edgeTup.get2(), new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt, EmptyMetaData.INSTANCE)).toList()), new LlvmMetadata(edgeTup.get4())); + XcfaEdge edge = new XcfaEdge(edgeTup.get1(), edgeTup.get2(), new SequenceLabel(stmts.stream().map(stmt -> new StmtLabel(stmt)).toList()), new LlvmMetadata(edgeTup.get4())); procedureBuilder.addEdge(edge); }); } diff --git a/subprojects/xcfa/llvm2xcfa/src/test/resources/c/12ptrtypes.c b/subprojects/xcfa/llvm2xcfa/src/test/resources/c/12ptrtypes.c index f42a220292..aa785ff812 100644 --- a/subprojects/xcfa/llvm2xcfa/src/test/resources/c/12ptrtypes.c +++ b/subprojects/xcfa/llvm2xcfa/src/test/resources/c/12ptrtypes.c @@ -1,6 +1,6 @@ void reach_error(){} -void check_geq_110(void* param) { +void check_geq_110(unsigned int* param) { if(*(unsigned int*)param <= 110) reach_error(); } diff --git a/subprojects/xcfa/xcfa-analysis/build.gradle.kts b/subprojects/xcfa/xcfa-analysis/build.gradle.kts index 1597b33abb..39e7ef2c12 100644 --- a/subprojects/xcfa/xcfa-analysis/build.gradle.kts +++ b/subprojects/xcfa/xcfa-analysis/build.gradle.kts @@ -22,6 +22,8 @@ dependencies { implementation(project(":theta-core")) implementation(project(":theta-analysis")) implementation(project(":theta-solver")) + implementation(project(":theta-solver-javasmt")) + implementation(project(":theta-solver-z3")) implementation(project(":theta-xcfa")) implementation(project(":theta-c-frontend")) testImplementation(project(":theta-c2xcfa")) 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 c27d7caac8..cf5d298dab 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 @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.analysis import hu.bme.mit.theta.analysis.expl.ExplState import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.pred.PredState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.core.decl.Decl import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.model.ImmutableValuation @@ -42,13 +43,17 @@ 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 when (sGlobal) { - is ExplState -> ExplState.of(sGlobal.getVal().changeVars(varLookup)) - is PredState -> PredState.of(sGlobal.preds.map { p -> p.changeVars(varLookup) }) + 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 -} class LazyDelegate(val getProperty: T.() -> P) { @@ -72,4 +77,7 @@ val XCFA.isInlined: Boolean by LazyDelegate { } } } -} \ No newline at end of file +} + +fun XcfaProcessState.foldVarLookup(): Map, VarDecl<*>> = + 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/XcfaAction.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAction.kt index 03760ce45e..82a8e65178 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAction.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAction.kt @@ -16,32 +16,44 @@ package hu.bme.mit.theta.xcfa.analysis -import hu.bme.mit.theta.analysis.expr.StmtAction +import hu.bme.mit.theta.analysis.ptr.PtrAction +import hu.bme.mit.theta.analysis.ptr.WriteTriples import hu.bme.mit.theta.core.stmt.Stmt import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.flatten -data class XcfaAction(val pid: Int, val edge: XcfaEdge) : StmtAction() { +data class XcfaAction +@JvmOverloads +constructor(val pid: Int, val edge: XcfaEdge, private val lastWrites: WriteTriples = emptyMap(), + private val nextCnt: Int = 0) : + PtrAction(lastWrites, nextCnt) { val source: XcfaLocation = edge.source val target: XcfaLocation = edge.target val label: XcfaLabel = edge.label private val stmts: List = label.toStmt().flatten() - constructor(pid: Int, source: XcfaLocation, target: XcfaLocation, - label: XcfaLabel = NopLabel) : this(pid, XcfaEdge(source, target, label)) + constructor(pid: Int, + source: XcfaLocation, + target: XcfaLocation, + label: XcfaLabel = NopLabel, + lastWrites: WriteTriples = emptyMap(), + nextCnt: Int = 0) : + this(pid, XcfaEdge(source, target, label), lastWrites, nextCnt) - override fun getStmts(): List { - return stmts - } + override val stmtList: List + get() = stmts override fun toString(): String { - return "$pid: $source -> $target [$label]" + return "$pid: $source -> $target [${getStmts()}]" } fun withLabel(sequenceLabel: SequenceLabel): XcfaAction { - return XcfaAction(pid, source, target, sequenceLabel) + return XcfaAction(pid, source, target, sequenceLabel, nextCnt = nextCnt) } + fun withLastWrites(writeTriples: WriteTriples, nextCnt: Int): XcfaAction { + return XcfaAction(pid, source, target, label, writeTriples, nextCnt) + } -} \ No newline at end of file +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaActionAdapter.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaActionAdapter.kt index 2d4abc4045..d5eb56d3e0 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaActionAdapter.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaActionAdapter.kt @@ -21,9 +21,8 @@ import com.google.gson.TypeAdapter import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonToken import com.google.gson.stream.JsonWriter +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.xcfa.model.XcfaEdge -import java.util.* -import kotlin.reflect.KClass class XcfaActionAdapter(val gsonSupplier: () -> Gson) : TypeAdapter() { @@ -41,6 +40,7 @@ class XcfaActionAdapter(val gsonSupplier: () -> Gson) : TypeAdapter( initGson() var pid: Int? = null lateinit var edge: XcfaEdge + lateinit var state: XcfaState> reader.beginObject() while (reader.peek() != JsonToken.END_OBJECT) { when (reader.nextName()) { diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt index 0f4cc5460c..2ddb10f763 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaAnalysis.kt @@ -25,10 +25,17 @@ import hu.bme.mit.theta.analysis.expl.ExplInitFunc import hu.bme.mit.theta.analysis.expl.ExplPrec import hu.bme.mit.theta.analysis.expl.ExplState import hu.bme.mit.theta.analysis.expl.ExplStmtTransFunc +import hu.bme.mit.theta.analysis.expr.ExprAction import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.expr.StmtAction import hu.bme.mit.theta.analysis.pred.* import hu.bme.mit.theta.analysis.pred.PredAbstractors.PredAbstractor +import hu.bme.mit.theta.analysis.ptr.PtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.analysis.ptr.getPtrInitFunc +import hu.bme.mit.theta.analysis.ptr.getPtrTransFunc import hu.bme.mit.theta.analysis.waitlist.Waitlist +import hu.bme.mit.theta.common.Try import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.decl.VarDecl @@ -36,9 +43,10 @@ import hu.bme.mit.theta.core.stmt.Stmts import hu.bme.mit.theta.core.type.booltype.BoolExprs.True import hu.bme.mit.theta.core.utils.TypeUtils import hu.bme.mit.theta.solver.Solver -import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.analysis.XcfaProcessState.Companion.createLookup import hu.bme.mit.theta.xcfa.analysis.coi.ConeOfInfluence +import hu.bme.mit.theta.xcfa.getFlatLabels +import hu.bme.mit.theta.xcfa.getGlobalVarsWithNeededMutexes import hu.bme.mit.theta.xcfa.isWritten import hu.bme.mit.theta.xcfa.model.* import hu.bme.mit.theta.xcfa.passes.changeVars @@ -46,34 +54,34 @@ import java.util.* import java.util.function.Predicate open class XcfaAnalysis( - private val corePartialOrd: PartialOrd>, - private val coreInitFunc: InitFunc, XcfaPrec

>, - private var coreTransFunc: TransFunc, XcfaAction, XcfaPrec

>, -) : Analysis, XcfaAction, XcfaPrec

> { + private val corePartialOrd: PartialOrd>>, + private val coreInitFunc: InitFunc>, XcfaPrec

>, + private var coreTransFunc: TransFunc>, XcfaAction, XcfaPrec

>, +) : Analysis>, XcfaAction, XcfaPrec

> { init { - ConeOfInfluence.coreTransFunc = transFunc as TransFunc, XcfaAction, XcfaPrec> - coreTransFunc = ConeOfInfluence.transFunc as TransFunc, XcfaAction, XcfaPrec

> + ConeOfInfluence.coreTransFunc = transFunc as TransFunc>, XcfaAction, XcfaPrec> + coreTransFunc = ConeOfInfluence.transFunc as TransFunc>, XcfaAction, XcfaPrec

> } - override fun getPartialOrd(): PartialOrd> = corePartialOrd - override fun getInitFunc(): InitFunc, XcfaPrec

> = coreInitFunc - override fun getTransFunc(): TransFunc, XcfaAction, XcfaPrec

> = coreTransFunc + override fun getPartialOrd(): PartialOrd>> = corePartialOrd + override fun getInitFunc(): InitFunc>, XcfaPrec

> = coreInitFunc + override fun getTransFunc(): TransFunc>, XcfaAction, XcfaPrec

> = coreTransFunc } /// Common private var tempCnt: Int = 0 -fun getCoreXcfaLts() = LTS, XcfaAction> { s -> +fun getCoreXcfaLts() = LTS>, XcfaAction> { s -> s.processes.map { proc -> if (proc.value.locs.peek().final) { listOf(XcfaAction(proc.key, XcfaEdge(proc.value.locs.peek(), proc.value.locs.peek(), SequenceLabel(listOf( proc.value.paramStmts.peek().second, ReturnLabel(proc.value.returnStmts.peek()), - ))))) + ))), nextCnt = s.sGlobal.nextCnt)) } else if (!proc.value.paramsInitialized) { listOf(XcfaAction(proc.key, XcfaEdge(proc.value.locs.peek(), proc.value.locs.peek(), - proc.value.paramStmts.peek().first))) + proc.value.paramStmts.peek().first), nextCnt = s.sGlobal.nextCnt)) } else { proc.value.locs.peek().outgoingEdges.map { edge -> val newLabel = edge.label.changeVars(proc.value.varLookup.peek()) @@ -101,30 +109,37 @@ fun getCoreXcfaLts() = LTS, XcfaAction> { s -> ?: error("No such method ${label.name}.") val lookup: MutableMap, VarDecl<*>> = LinkedHashMap() SequenceLabel(listOf(procedure.params.withIndex() - .filter { it.value.second != ParamDirection.OUT }.map { iVal -> + .filter { it.value.second != ParamDirection.OUT }.mapNotNull { iVal -> val originalVar = iVal.value.first val tempVar = Var("tmp${tempCnt++}_" + originalVar.name, originalVar.type) lookup[originalVar] = tempVar - StmtLabel( - Stmts.Assign( - TypeUtils.cast(tempVar, tempVar.type), - TypeUtils.cast(label.params[iVal.index], tempVar.type)), - metadata = label.metadata) + val trial = Try.attempt { + StmtLabel( + Stmts.Assign( + TypeUtils.cast(tempVar, tempVar.type), + TypeUtils.cast(label.params[iVal.index], tempVar.type)), + metadata = label.metadata) + } + if (trial.isSuccess) { + trial.asSuccess().value + } else { + null + } }, listOf(label.copy(tempLookup = lookup))).flatten()) } else label }) - XcfaAction(proc.key, edge.withLabel(newNewLabel)) + XcfaAction(proc.key, edge.withLabel(newNewLabel), nextCnt = s.sGlobal.nextCnt) } else - XcfaAction(proc.key, edge.withLabel(newLabel)) + XcfaAction(proc.key, edge.withLabel(newLabel), nextCnt = s.sGlobal.nextCnt) } } }.flatten().toSet() } -fun getXcfaLts(): LTS, XcfaAction> { +fun getXcfaLts(): LTS>, XcfaAction> { val lts = getCoreXcfaLts() - return LTS, XcfaAction> { s -> + return LTS>, XcfaAction> { s -> lts.getEnabledActionsFor(s).filter { !s.apply(it).first.bottom }.toSet() } } @@ -137,12 +152,12 @@ enum class ErrorDetection { } fun getXcfaErrorPredicate( - errorDetection: ErrorDetection): Predicate> = when (errorDetection) { + errorDetection: ErrorDetection): Predicate>> = when (errorDetection) { ErrorDetection.ERROR_LOCATION -> - Predicate> { s -> s.processes.any { it.value.locs.peek().error } } + Predicate>> { s -> s.processes.any { it.value.locs.peek().error } } ErrorDetection.DATA_RACE -> { - Predicate> { s -> + Predicate>> { s -> val xcfa = s.xcfa!! for (process1 in s.processes) for (process2 in s.processes) @@ -164,32 +179,33 @@ fun getXcfaErrorPredicate( } } - ErrorDetection.NO_ERROR, ErrorDetection.OVERFLOW -> Predicate> { false } + ErrorDetection.NO_ERROR, ErrorDetection.OVERFLOW -> Predicate>> { false } } -fun getPartialOrder(partialOrd: PartialOrd) = - PartialOrd> { s1, s2 -> +fun getPartialOrder(partialOrd: PartialOrd>) = + PartialOrd>> { s1, s2 -> s1.processes == s2.processes && s1.bottom == s2.bottom && s1.mutexes == s2.mutexes && partialOrd.isLeq( s1.sGlobal, s2.sGlobal) } -private fun stackIsLeq(s1: XcfaState, s2: XcfaState) = s2.processes.keys.all { pid -> +private fun stackIsLeq(s1: XcfaState>, + s2: XcfaState>) = s2.processes.keys.all { pid -> s1.processes[pid]?.let { ps1 -> val ps2 = s2.processes.getValue(pid) ps1.locs.peek() == ps2.locs.peek() && ps1.paramsInitialized && ps2.paramsInitialized } ?: false } -fun getStackPartialOrder(partialOrd: PartialOrd) = - PartialOrd> { s1, s2 -> +fun getStackPartialOrder(partialOrd: PartialOrd>) = + PartialOrd>> { s1, s2 -> s1.processes.size == s2.processes.size && stackIsLeq(s1, s2) && s1.bottom == s2.bottom && s1.mutexes == s2.mutexes && partialOrd.isLeq(s1.withGeneralizedVars(), s2.withGeneralizedVars()) } -private fun , P : XcfaPrec> getXcfaArgBuilder( +private fun >, P : XcfaPrec> getXcfaArgBuilder( analysis: Analysis, - lts: LTS, XcfaAction>, + lts: LTS>, XcfaAction>, errorDetection: ErrorDetection) : ArgBuilder = ArgBuilder.create( @@ -198,14 +214,14 @@ private fun , P : XcfaPrec> getXcfaArgBui getXcfaErrorPredicate(errorDetection) ) -fun , P : XcfaPrec> getXcfaAbstractor( +fun >, P : XcfaPrec> getXcfaAbstractor( analysis: Analysis, waitlist: Waitlist<*>, stopCriterion: StopCriterion<*, *>, logger: Logger, - lts: LTS, XcfaAction>, + lts: LTS>, XcfaAction>, errorDetection: ErrorDetection -): Abstractor, XcfaAction, out XcfaPrec> = +): Abstractor>, XcfaAction, out XcfaPrec> = XcfaAbstractor.builder(getXcfaArgBuilder(analysis, lts, errorDetection)) .waitlist(waitlist as Waitlist>) // TODO: can we do this nicely? .stopCriterion(stopCriterion as StopCriterion).logger(logger) @@ -218,7 +234,7 @@ fun , P : XcfaPrec> getXcfaAbstractor( /// EXPL private fun getExplXcfaInitFunc(xcfa: XCFA, - solver: Solver): (XcfaPrec) -> List> { + solver: Solver): (XcfaPrec>) -> List>> { val processInitState = xcfa.initProcedures.mapIndexed { i, it -> val initLocStack: LinkedList = LinkedList() initLocStack.add(it.first.initLoc) @@ -226,14 +242,15 @@ private fun getExplXcfaInitFunc(xcfa: XCFA, varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))))) }.toMap() return { p -> - ExplInitFunc.create(solver, True()).getInitStates(p.p) + ExplInitFunc.create(solver, True()).getPtrInitFunc().getInitStates(p.p) .map { XcfaState(xcfa, processInitState, it) } } } -private fun getExplXcfaTransFunc(solver: Solver, - maxEnum: Int): (XcfaState, XcfaAction, XcfaPrec) -> List> { - val explTransFunc = ExplStmtTransFunc.create(solver, maxEnum) +private fun getExplXcfaTransFunc(solver: Solver, maxEnum: Int, isHavoc: Boolean): + (XcfaState>, XcfaAction, XcfaPrec>) -> List>> { + val explTransFunc = (ExplStmtTransFunc.create(solver, + maxEnum) as TransFunc).getPtrTransFunc(isHavoc) return { s, a, p -> val (newSt, newAct) = s.apply(a) explTransFunc.getSuccStates(newSt.sGlobal, newAct, p.p.addVars( @@ -243,16 +260,17 @@ private fun getExplXcfaTransFunc(solver: Solver, } class ExplXcfaAnalysis(xcfa: XCFA, solver: Solver, maxEnum: Int, - partialOrd: PartialOrd>) : XcfaAnalysis( - corePartialOrd = partialOrd, - coreInitFunc = getExplXcfaInitFunc(xcfa, solver), - coreTransFunc = getExplXcfaTransFunc(solver, maxEnum) -) + partialOrd: PartialOrd>>, isHavoc: Boolean) : + XcfaAnalysis>( + corePartialOrd = partialOrd, + coreInitFunc = getExplXcfaInitFunc(xcfa, solver), + coreTransFunc = getExplXcfaTransFunc(solver, maxEnum, isHavoc) + ) /// PRED private fun getPredXcfaInitFunc(xcfa: XCFA, - predAbstractor: PredAbstractor): (XcfaPrec) -> List> { + predAbstractor: PredAbstractor): (XcfaPrec>) -> List>> { val processInitState = xcfa.initProcedures.mapIndexed { i, it -> val initLocStack: LinkedList = LinkedList() initLocStack.add(it.first.initLoc) @@ -260,25 +278,27 @@ private fun getPredXcfaInitFunc(xcfa: XCFA, varLookup = LinkedList(listOf(createLookup(it.first, "T$i", ""))))) }.toMap() return { p -> - PredInitFunc.create(predAbstractor, True()).getInitStates(p.p) + PredInitFunc.create(predAbstractor, True()).getPtrInitFunc().getInitStates(p.p) .map { XcfaState(xcfa, processInitState, it) } } } -private fun getPredXcfaTransFunc( - predAbstractor: PredAbstractors.PredAbstractor): (XcfaState, XcfaAction, XcfaPrec) -> List> { - val predTransFunc = PredTransFunc.create(predAbstractor) +private fun getPredXcfaTransFunc(predAbstractor: PredAbstractors.PredAbstractor, isHavoc: Boolean): + (XcfaState>, XcfaAction, XcfaPrec>) -> List>> { + val predTransFunc = (PredTransFunc.create( + predAbstractor) as TransFunc).getPtrTransFunc(isHavoc) return { s, a, p -> val (newSt, newAct) = s.apply(a) predTransFunc.getSuccStates(newSt.sGlobal, newAct, p.p.addVars( - listOf(s.processes.map { it.value.varLookup }.flatten(), - listOf(getTempLookup(a.label))).flatten())).map { newSt.withState(it) } + s.processes.map { it.value.foldVarLookup() + getTempLookup(a.label) } + )).map { newSt.withState(it) } } } class PredXcfaAnalysis(xcfa: XCFA, solver: Solver, predAbstractor: PredAbstractor, - partialOrd: PartialOrd>) : XcfaAnalysis( - corePartialOrd = partialOrd, - coreInitFunc = getPredXcfaInitFunc(xcfa, predAbstractor), - coreTransFunc = getPredXcfaTransFunc(predAbstractor) -) + partialOrd: PartialOrd>>, isHavoc: Boolean) : + XcfaAnalysis>( + corePartialOrd = partialOrd, + coreInitFunc = getPredXcfaInitFunc(xcfa, predAbstractor), + coreTransFunc = getPredXcfaTransFunc(predAbstractor, isHavoc) + ) 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 2090bc25e0..64e8984d4d 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 @@ -25,37 +25,38 @@ import hu.bme.mit.theta.analysis.expr.refinement.PrecRefiner import hu.bme.mit.theta.analysis.expr.refinement.Refutation import hu.bme.mit.theta.analysis.expr.refinement.RefutationToPrec import hu.bme.mit.theta.analysis.pred.PredPrec +import hu.bme.mit.theta.analysis.ptr.PtrPrec import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.xcfa.model.* +import hu.bme.mit.theta.core.type.Expr +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) : - PrecRefiner, XcfaAction, XcfaPrec

, R> { +class XcfaPrecRefiner(refToPrec: RefutationToPrec, R>) : + PrecRefiner, XcfaAction, XcfaPrec>, R> { - private val refToPrec: RefutationToPrec = Preconditions.checkNotNull(refToPrec) + private val refToPrec: RefutationToPrec, R> = Preconditions.checkNotNull(refToPrec) - override fun refine(prec: XcfaPrec

, trace: Trace, XcfaAction>, - refutation: R): XcfaPrec

{ + override fun refine(prec: XcfaPrec>, trace: Trace, XcfaAction>, + refutation: R): XcfaPrec> { Preconditions.checkNotNull(trace) Preconditions.checkNotNull(prec) Preconditions.checkNotNull(refutation) - val checkForPop = !(trace.states.first() as XcfaState<*>).xcfa!!.isInlined - var runningPrec: P = prec.p + var runningPrec: PtrPrec

= prec.p for (i in trace.states.indices) { - val reverseLookup = trace.states[i].processes.values.map { - it.varLookup.map { - it.map { - Pair(it.value, it.key) - } - }.flatten() + val reverseVarLookup = trace.states[i].processes.values.map { + it.foldVarLookup().map { Pair(it.value, it.key) } }.flatten().toMap() - val additionalLookup = if (i > 0) getTempLookup( + val reverseTempLookup = if (i > 0) getTempLookup( trace.actions[i - 1].edge.label).entries.associateBy( { it.value }) { it.key } else emptyMap() - val varLookup = if (checkForPop) additionalLookup else (reverseLookup + additionalLookup) - val precFromRef = refToPrec.toPrec(refutation, i).changeVars(varLookup) + 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) } @@ -64,12 +65,16 @@ class XcfaPrecRefiner(refToPrec: Refuta } } +private fun Expr<*>.hasDeref(): Boolean = + 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.") } @@ -83,5 +88,7 @@ fun

P.addVars(lookups: Collection, VarDecl<*>>>): P = is PredPrec -> PredPrec.of( preds.map { lookups.map { lookup -> it.changeVars(lookup) } }.flatten()) as P + is PtrPrec<*> -> PtrPrec(innerPrec.addVars(lookups)) as P + else -> error("Precision type ${this.javaClass} not supported.") } diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingeExprTraceRefiner.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingeExprTraceRefiner.kt index 32ef97a74f..e212d590c9 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingeExprTraceRefiner.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/XcfaSingeExprTraceRefiner.kt @@ -23,8 +23,11 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.RefinerResult import hu.bme.mit.theta.analysis.expr.ExprAction import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.expr.refinement.* +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.analysis.ptr.WriteTriples +import hu.bme.mit.theta.analysis.ptr.patch import hu.bme.mit.theta.common.logging.Logger -import java.util.LinkedList +import java.util.* class XcfaSingleExprTraceRefiner : @@ -61,6 +64,60 @@ class XcfaSingleExprTraceRefiner, prec: P?): RefinerResult { + Preconditions.checkNotNull(arg) + Preconditions.checkNotNull(prec) + assert(!arg.isSafe) { "ARG must be unsafe" } + val optionalNewCex = arg.cexs.findFirst() + val cexToConcretize = optionalNewCex.get() + val rawTrace = cexToConcretize.toTrace() + val (_, states, actions) = rawTrace.actions.foldIndexed( + Triple(Pair(emptyMap(), 0), listOf(rawTrace.getState(0)), + listOf())) { i: Int, (wTripleCnt: Pair, states: List, actions: List): Triple, List, List>, a: A -> + val (wTriple, cnt) = wTripleCnt + val newA = (a as XcfaAction).withLastWrites(wTriple, cnt) + val newState = (rawTrace.getState(i + 1) as XcfaState>).let { + it.withState(PtrState(it.sGlobal.innerState.patch(newA.nextWriteTriples()))) + } + Triple(Pair(newA.nextWriteTriples(), newA.cnts.values.maxOrNull() ?: newA.inCnt), states + (newState as S), + actions + (newA as A)) + } + val traceToConcretize = Trace.of(states, actions) + + logger.write(Logger.Level.INFO, "| | Trace length: %d%n", traceToConcretize.length()) + logger.write(Logger.Level.DETAIL, "| | Trace: %s%n", traceToConcretize) + logger.write(Logger.Level.SUBSTEP, "| | Checking trace...") + val cexStatus = exprTraceChecker.check(traceToConcretize) + logger.write(Logger.Level.SUBSTEP, "done, result: %s%n", cexStatus) + assert(cexStatus.isFeasible() || cexStatus.isInfeasible()) { "Unknown CEX status" } + return if (cexStatus.isFeasible()) { + RefinerResult.unsafe(traceToConcretize) + } else { + val refutation = cexStatus.asInfeasible().refutation + logger.write(Logger.Level.DETAIL, "| | | Refutation: %s%n", refutation) + val refinedPrec = precRefiner.refine(prec, traceToConcretize, refutation) + val pruneIndex = refutation.getPruneIndex() + assert(0 <= pruneIndex) { "Pruning index must be non-negative" } + assert(pruneIndex <= cexToConcretize.length()) { "Pruning index larger than cex length" } + when (pruneStrategy) { + PruneStrategy.LAZY -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning from index %d...", pruneIndex) + val nodeToPrune = cexToConcretize.node(pruneIndex) + nodePruner.prune(arg, nodeToPrune) + } + + PruneStrategy.FULL -> { + logger.write(Logger.Level.SUBSTEP, "| | Pruning whole ARG", pruneIndex) + arg.pruneAll() + } + + else -> throw java.lang.UnsupportedOperationException("Unsupported pruning strategy") + } + logger.write(Logger.Level.SUBSTEP, "done%n") + RefinerResult.spurious(refinedPrec) + } + } + override fun refine(arg: ARG, prec: P?): RefinerResult { Preconditions.checkNotNull(arg) Preconditions.checkNotNull

(prec) @@ -70,7 +127,7 @@ class XcfaSingleExprTraceRefiner).xcfa!!.isInlined return if (checkForPop && refinerResult.isUnsafe) findPoppedState(traceToConcretize)?.let { (i, state) -> 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 8a72624339..0054be37f2 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 @@ -167,12 +167,12 @@ data class XcfaState @JvmOverloads constructor( /* 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)), metadata = EmptyMetaData) + 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)), metadata = EmptyMetaData) + cast(it.key.changeVars(lookup).ref, it.key.type))) }), )))) val newMutexes = LinkedHashMap(mutexes) @@ -264,12 +264,12 @@ data class XcfaProcessState(val locs: LinkedList, val varLookup: L /* 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)), metadata = EmptyMetaData) + 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)), metadata = EmptyMetaData) + cast(it.key.changeVars(lookup).ref, it.key.type))) }), )) return copy(locs = deque, varLookup = varLookup, returnStmts = returnStmts, paramStmts = paramStmts, diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoi.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoi.kt index 4a7df7733f..54b9e7f658 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoi.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoi.kt @@ -20,9 +20,11 @@ import hu.bme.mit.theta.analysis.LTS import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.TransFunc import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.AssignStmt import hu.bme.mit.theta.core.stmt.HavocStmt +import hu.bme.mit.theta.core.type.LitExpr import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaPrec @@ -36,7 +38,7 @@ import kotlin.math.min lateinit var ConeOfInfluence: XcfaCoi -internal typealias S = XcfaState +internal typealias S = XcfaState> internal typealias A = XcfaAction internal var XcfaAction.transFuncVersion: XcfaAction? by nullableExtension() @@ -48,7 +50,7 @@ abstract class XcfaCoi(protected val xcfa: XCFA) { protected var lastPrec: Prec? = null protected var XcfaLocation.scc: Int by extension() - protected val directObservation: MutableMap> = mutableMapOf() + protected val directObservers: MutableMap> = mutableMapOf() abstract val lts: LTS @@ -104,37 +106,55 @@ abstract class XcfaCoi(protected val xcfa: XCFA) { protected fun findDirectObservers(edge: XcfaEdge, prec: Prec) { val precVars = prec.usedVars - val writtenVars = edge.collectVarsWithAccessType().filter { it.value.isWritten && it.key in precVars } + val writtenVars = edge.collectVarsWithAccessType() + .filter { it.value.isWritten && it.key in precVars } // TODO deref it.key in prec? if (writtenVars.isEmpty()) return + val writtenMemLocs = writtenVars.pointsTo(xcfa) - val toVisit = mutableListOf(edge) + val toVisit = edge.target.outgoingEdges.toMutableList() val visited = mutableSetOf() while (toVisit.isNotEmpty()) { val visiting = toVisit.removeFirst() visited.add(visiting) - addEdgeIfObserved(edge, visiting, writtenVars, precVars, directObservation) + val currentVars = visiting.collectVarsWithAccessType() + addEdgeIfObserved(edge, visiting, writtenVars, writtenMemLocs, precVars, directObservers, currentVars) + + if (writtenMemLocs.size <= 1) { + val currentWrites = currentVars.filter { it.value.isWritten }.map { it.key } + if (writtenVars.all { it.key in currentWrites }) { + val currentMemWrites = currentWrites.pointsTo(xcfa) + if (currentMemWrites.size <= 1 && writtenMemLocs.all { it in currentMemWrites }) { + continue + } + } + } + toVisit.addAll(visiting.target.outgoingEdges.filter { it !in visited }) } } protected open fun addEdgeIfObserved( - source: XcfaEdge, target: XcfaEdge, observableVars: Map, AccessType>, - precVars: Collection>, relation: MutableMap> + source: XcfaEdge, target: XcfaEdge, observableVars: VarAccessMap, + writtenMemLocs: Set>, precVars: Collection>, + relation: MutableMap>, vars: VarAccessMap = target.collectVarsWithAccessType() ) { - val vars = target.collectVarsWithAccessType() var relevantAction = vars.any { it.value.isWritten && it.key in precVars } if (!relevantAction) { val assumeVars = target.label.collectAssumesVars() relevantAction = assumeVars.any { it in precVars } } - if (relevantAction && vars.any { it.key in observableVars && it.value.isRead }) { + val readVars = vars.filter { it.value.isRead } + if (relevantAction && (readVars.any { it.key in observableVars } || + readVars.pointsTo(xcfa).any { it in writtenMemLocs })) { addToRelation(source, target, relation) } } - protected abstract fun addToRelation(source: XcfaEdge, target: XcfaEdge, - relation: MutableMap>) + protected abstract fun addToRelation( + source: XcfaEdge, target: XcfaEdge, + relation: MutableMap> + ) protected fun isRealObserver(edge: XcfaEdge) = edge.label.collectAssumesVars().isNotEmpty() 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 c13cc33106..0500b5d88b 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 @@ -25,6 +25,7 @@ import hu.bme.mit.theta.xcfa.model.StartLabel import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.model.XcfaEdge import hu.bme.mit.theta.xcfa.model.XcfaProcedure +import hu.bme.mit.theta.xcfa.pointsTo class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { @@ -35,7 +36,7 @@ class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { set(value) { edgeToProcedure[this] = value } - private val interProcessObservation: MutableMap> = mutableMapOf() + private val interProcessObservers: MutableMap> = mutableMapOf() data class ProcedureEntry( val procedure: XcfaProcedure, @@ -101,8 +102,8 @@ class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { if (isRealObserver(visiting)) return true visited.add(visiting) - val toAdd = (directObservation[visiting] ?: emptySet()) union - (interProcessObservation[visiting]?.filter { edge -> + 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) @@ -126,8 +127,8 @@ class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { } fun reinitialize(prec: Prec) { - directObservation.clear() - interProcessObservation.clear() + directObservers.clear() + interProcessObservers.clear() xcfa.procedures.forEach { procedure -> procedure.edges.forEach { edge -> edge.procedure = procedure @@ -143,17 +144,16 @@ class XcfaCoiMultiThread(xcfa: XCFA) : XcfaCoi(xcfa) { 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, precVars, interProcessObservation) + addEdgeIfObserved(edge, it, writtenVars, writtenMemLocs, precVars, interProcessObservers) } } } - override fun addToRelation(source: XcfaEdge, target: XcfaEdge, - relation: MutableMap>) { - relation[source] = relation[source] ?: mutableSetOf() - relation[source]!!.add(target) + override fun addToRelation(source: XcfaEdge, target: XcfaEdge, relation: MutableMap>) { + relation[source] = relation.getOrDefault(source, setOf()) + target } } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiSingleThread.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiSingleThread.kt index 779a77ab52..9759e5fd60 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiSingleThread.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/coi/XcfaCoiSingleThread.kt @@ -51,7 +51,7 @@ class XcfaCoiSingleThread(xcfa: XCFA) : XcfaCoi(xcfa) { fun reinitialize(prec: Prec) { lastPrec = prec - directObservation.clear() + directObservers.clear() val realObservers = mutableSetOf() xcfa.procedures.forEach { procedure -> procedure.edges.forEach { edge -> @@ -64,10 +64,8 @@ class XcfaCoiSingleThread(xcfa: XCFA) : XcfaCoi(xcfa) { collectedObservedEdges(realObservers) } - override fun addToRelation(source: XcfaEdge, target: XcfaEdge, - relation: MutableMap>) { - relation[target] = relation[target] ?: mutableSetOf() - relation[target]!!.add(source) + override fun addToRelation(source: XcfaEdge, target: XcfaEdge, relation: MutableMap>) { + relation[target] = relation.getOrDefault(target, setOf()) + source } private fun collectedObservedEdges(realObservers: Set) { @@ -76,7 +74,7 @@ class XcfaCoiSingleThread(xcfa: XCFA) : XcfaCoi(xcfa) { while (toVisit.isNotEmpty()) { val visiting = toVisit.removeFirst() visited.add(visiting) - val toAdd = directObservation[visiting] ?: emptySet() + val toAdd = directObservers[visiting] ?: emptySet() toVisit.addAll(toAdd.filter { it !in visited }) } observed = visited.map { it.source to it.target }.toSet() diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaFurtherOptimizer.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaFurtherOptimizer.kt new file mode 100644 index 0000000000..b62313c358 --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaFurtherOptimizer.kt @@ -0,0 +1,47 @@ +/* + * 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.XCFA +import hu.bme.mit.theta.xcfa.model.XcfaBuilder +import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder +import hu.bme.mit.theta.xcfa.passes.ProcedurePass +import hu.bme.mit.theta.xcfa.passes.ProcedurePassManager + +internal fun XCFA.optimizeFurther(passes: List): XCFA { + if (passes.isEmpty()) return this + val passManager = ProcedurePassManager(passes) + val copy: XcfaProcedureBuilder.() -> XcfaProcedureBuilder = { + XcfaProcedureBuilder( + name = name, + manager = passManager, + params = getParams().toMutableList(), + vars = getVars().toMutableSet(), + locs = getLocs().toMutableSet(), + edges = getEdges().toMutableSet(), + metaData = metaData.toMutableMap() + ).also { it.copyMetaLocs(this) } + } + + val builder = XcfaBuilder(name, vars.toMutableSet()) + procedureBuilders.forEach { builder.addProcedure(it.copy()) } + initProcedureBuilders.forEach { (proc, params) -> + val initProc = builder.getProcedures().find { it.name == proc.name } ?: proc.copy() + builder.addEntryPoint(initProc, params) + } + return builder.build() +} \ No newline at end of file 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 new file mode 100644 index 0000000000..c44657b9ac --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcChecker.kt @@ -0,0 +1,379 @@ +/* + * 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.SafetyChecker +import hu.bme.mit.theta.analysis.algorithm.SafetyResult +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.stmt.AssignStmt +import hu.bme.mit.theta.core.stmt.AssumeStmt +import hu.bme.mit.theta.core.stmt.HavocStmt +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.RefExpr +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.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.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 + +private val Expr<*>.vars get() = ExprUtils.getVars(this) + +class XcfaOcChecker(xcfa: XCFA, decisionProcedure: OcDecisionProcedureType, private val logger: Logger) : + SafetyChecker>, XcfaAction, XcfaPrec> { + + private val ocChecker: OcChecker = decisionProcedure.checker() + private val solver = ocChecker.solver + + private val xcfa: XCFA = xcfa.optimizeFurther( + listOf(AssumeFalseRemovalPass(), MutexToVarPass(), AtomicReadsOneWritePass()) + ) + private var indexing = VarIndexingFactory.indexing(0) + private val localVars = mutableMapOf, MutableMap>>() + + private val threads = mutableSetOf() + private val events = mutableMapOf, MutableMap>>() + private val violations = mutableListOf() // OR! + private val branchingConditions = mutableListOf>() + private val pos = mutableListOf() + private val rfs = mutableMapOf, MutableSet>() + + override fun check(prec: XcfaPrec?): SafetyResult>, XcfaAction> = let { + if (xcfa.initProcedures.size > 1) error("Multiple entry points are not supported by OC checker.") + + logger.write(Logger.Level.MAINSTEP, "Adding constraints...\n") + xcfa.initProcedures.forEach { processThread(Thread(it.first)) } + addCrossThreadRelations() + if (!addToSolver()) return@let SafetyResult.safe() // no violations in the model + + logger.write(Logger.Level.MAINSTEP, "Start checking...\n") + val status = ocChecker.check(events, pos, rfs) + when { + status?.isUnsat == true -> SafetyResult.safe() + status?.isSat == true -> SafetyResult.unsafe( + XcfaOcTraceExtractor(xcfa, ocChecker, threads, events, violations, pos).trace + ) + + 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 -> + check(!inEdge || last.size == 1) + val decl = d.threadVar(pid) + val useLastClk = inEdge || atomicEntered == true + val e = + if (useLastClk) E(decl.getNewIndexed(), type, guard, pid, edge, last.first().clkId) + else E(decl.getNewIndexed(), type, guard, pid, edge) + last.forEach { po(it, e) } + inEdge = true + if (atomicEntered == false) atomicEntered = true + when (type) { + 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) + } + + 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 + } + + if (current.loc.final) { + thread.finalEvents.addAll(current.lastEvents) + } + + 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.") + } + 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 + } + 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) + } + } + } + stmt.cond.vars.forEach { + last = newEvent(it, EventType.READ) + } + if (edge.source.outgoingEdges.size == 1 && asAssign) { + last.first().assignment = condWithConsts + } + } + + is HavocStmt<*> -> { + last = newEvent(stmt.varDecl, EventType.WRITE) + } + + else -> error("Unsupported 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.") + } + 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 FenceLabel -> { + if (label.labels.size > 1 || label.labels.firstOrNull()?.contains("ATOMIC") != true) { + error("Untransformed fence label: $label") + } + if (label.isAtomicBegin) atomicEntered = false + if (label.isAtomicEnd) atomicEntered = null + } + + is NopLabel -> {} + else -> error("Unsupported label type by OC checker: $label") + } + 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) + } + } + + 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.") + } + } + 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 + } + + 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) + } + + private fun addToSolver(): Boolean { + if (violations.isEmpty()) return false + + // Value assignment + events.values.flatMap { it.values.flatten() }.filter { it.assignment != null }.forEach { event -> + if (event.guard.isEmpty()) solver.add(event.assignment) + else solver.add(Imply(event.guardExpr, event.assignment)) + } + + // Branching conditions + branchingConditions.forEach { solver.add(it) } + + // Property violation + solver.add(Or(violations.map { it.guard })) + + // RF + rfs.forEach { (_, 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 + } + solver.add(Imply(event.guardExpr, Or(rels.map { it.declRef }))) // RF-Some + } + } + + return true + } + + // Utility functions + + private fun po(from: E?, to: E) { + from ?: return + pos.add(Relation(RelationType.PO, from, to)) + } + + private fun List>>.merge(merge: (Set, Set) -> Set = { a, b -> a + b }) = + reduce(mapOf()) { acc, map -> + (acc.keys + map.keys).associateWith { k -> + val set1 = acc[k] ?: setOf() + val set2 = map[k] ?: setOf() + merge(set1, set2) + } + } + + private inline fun Collection.reduce(default: T, operation: (T, T) -> T): T = + if (isEmpty()) default else reduce(operation) + + 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 + } + 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 + cast(localVars.getOrPut(this) { mutableMapOf() }.getOrPut(pid) { + Decls.Var("t$pid::$name", type) + }, type) + } else this + + private fun VarDecl.getNewIndexed(increment: Boolean = true): IndexedConstDecl { + val constDecl = getConstDecl(indexing.get(this)) + if (increment) indexing = indexing.inc(this) + return constDecl + } +} 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 new file mode 100644 index 0000000000..05ff67fc9e --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTraceExtractor.kt @@ -0,0 +1,185 @@ +/* + * 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.Trace +import hu.bme.mit.theta.analysis.algorithm.oc.OcChecker +import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.ptr.PtrState +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.booltype.BoolLitExpr +import hu.bme.mit.theta.xcfa.analysis.XcfaAction +import hu.bme.mit.theta.xcfa.analysis.XcfaProcessState +import hu.bme.mit.theta.xcfa.analysis.XcfaState +import hu.bme.mit.theta.xcfa.getFlatLabels +import hu.bme.mit.theta.xcfa.isAtomicBegin +import hu.bme.mit.theta.xcfa.isAtomicEnd +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 java.util.* + +/** + * 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 +) { + + 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) + } + } + + return Trace.of(stateList, actionList) + } + + 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 (top.eventsToVisit!!.isEmpty()) { + stack.pop() + finished.add(top.event) + continue + } + + 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 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 + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000000..19858f5a03 --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/oc/XcfaOcTypes.kt @@ -0,0 +1,115 @@ +/* + * 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.core.decl.IndexedConstDecl +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 +import hu.bme.mit.theta.core.type.booltype.BoolExprs.And +import hu.bme.mit.theta.core.type.booltype.BoolExprs.Or +import hu.bme.mit.theta.core.type.booltype.BoolType +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 typealias E = XcfaEvent +internal typealias R = Relation + +@Suppress("unused") +enum class OcDecisionProcedureType(internal val checker: () -> OcChecker) { + + BASIC({ BasicOcChecker() }), + PROPAGATOR({ UserPropagatorOcChecker() }), + PREVENTIVE({ PreventivePropagatorOcChecker() }), +} + +/** + * 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. + */ +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() +) : Event(const, type, guard, pid, clkId) { + + companion object { + + private var cnt: Int = 0 + private fun uniqueId(): Int = cnt++ + } +} + +internal data class Violation( + 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 finalEvents: MutableSet = mutableSetOf() + + companion object { + + private var cnt: Int = 0 + private 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 +} + +internal data class StackItem(val event: XcfaEvent) { + + var eventsToVisit: MutableList? = null +} diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporCoiLts.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporCoiLts.kt index 6586ffb114..4843b1118b 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporCoiLts.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporCoiLts.kt @@ -18,8 +18,8 @@ package hu.bme.mit.theta.xcfa.analysis.por import hu.bme.mit.theta.analysis.LTS import hu.bme.mit.theta.analysis.expr.ExprState -import hu.bme.mit.theta.core.decl.Decl -import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaState import hu.bme.mit.theta.xcfa.analysis.coi.transFuncVersion @@ -28,17 +28,13 @@ import hu.bme.mit.theta.xcfa.model.XcfaEdge class XcfaAasporCoiLts( xcfa: XCFA, - ignoredVarRegistry: MutableMap, MutableSet>, - coiLTS: LTS, XcfaAction> + ignoredVarRegistry: MutableMap, MutableSet>, + coiLTS: LTS>, XcfaAction> ) : XcfaAasporLts(xcfa, ignoredVarRegistry) { init { simpleXcfaLts = coiLTS } - override fun getEdgeOf(action: XcfaAction): XcfaEdge = - super.getEdgeOf(action.transFuncVersion ?: action) - - override fun isBackwardAction(action: XcfaAction): Boolean = - backwardTransitions.any { it.source == action.edge.source && it.target == action.edge.target } + override fun getEdge(action: XcfaAction): XcfaEdge = super.getEdge(action.transFuncVersion ?: action) } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporLts.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporLts.kt index 519a61d70c..1861b5fe56 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporLts.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaAasporLts.kt @@ -17,19 +17,22 @@ package hu.bme.mit.theta.xcfa.analysis.por import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.expr.ExprState -import hu.bme.mit.theta.core.decl.Decl -import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaState import hu.bme.mit.theta.xcfa.model.XCFA open class XcfaAasporLts( xcfa: XCFA, - private val ignoredVarRegistry: MutableMap, MutableSet> + private val ignoredVarRegistry: MutableMap, MutableSet> ) : XcfaSporLts(xcfa) { - override fun

getEnabledActionsFor(state: XcfaState<*>, exploredActions: Collection, - prec: P): Set { + override fun

getEnabledActionsFor( + state: XcfaState>, + exploredActions: Collection, + prec: P + ): Set { // Collecting enabled actions val allEnabledActions = simpleXcfaLts.getEnabledActionsFor(state, exploredActions, prec) @@ -41,20 +44,20 @@ open class XcfaAasporLts( } else { setOf(exploredActions) } - var finalIgnoredVars = setOf>() + var finalIgnoredVars = setOf>() // Calculate source sets from all possible starting action set for (firstActions in sourceSetFirstActions) { // Variables that have been ignored (if they would be in the precision, more actions have had to be added to the source set) - val ignoredVars = mutableSetOf>() - val sourceSet = calculateSourceSet(allEnabledActions, firstActions, prec, ignoredVars) + val ignoredVars = mutableSetOf>() + val sourceSet = calculateSourceSet(state, allEnabledActions, firstActions, prec, ignoredVars) if (minimalSourceSet.isEmpty() || sourceSet.size < minimalSourceSet.size) { minimalSourceSet = sourceSet.toMutableSet() finalIgnoredVars = ignoredVars } } finalIgnoredVars.forEach { ignoredVar -> - if (!ignoredVarRegistry.containsKey(ignoredVar)) { + if (ignoredVar !in ignoredVarRegistry) { ignoredVarRegistry[ignoredVar] = mutableSetOf() } checkNotNull(ignoredVarRegistry[ignoredVar]).add(state) @@ -72,16 +75,19 @@ open class XcfaAasporLts( * @param ignoredVars variables that have been ignored (if they would be in the precision, more actions have had to be added to the source set) * @return a source set of enabled actions in the current abstraction */ - private fun calculateSourceSet(enabledActions: Collection, firstActions: Collection, - prec: Prec, ignoredVars: MutableSet>): Set { - if (firstActions.any(this::isBackwardAction)) { + private fun calculateSourceSet( + state: XcfaState>, + enabledActions: Collection, firstActions: Collection, + prec: Prec, ignoredVars: MutableSet> + ): Set { + if (firstActions.any { it.isBackward }) { return enabledActions.toSet() } val sourceSet = firstActions.toMutableSet() val otherActions = enabledActions.toMutableSet() // actions not in the source set firstActions.forEach(otherActions::remove) - val ignoredVarsByAction = otherActions.associateWith { mutableSetOf>() } + val ignoredVarsByAction = otherActions.associateWith { mutableSetOf>() } var addedNewAction = true while (addedNewAction) { @@ -90,10 +96,10 @@ open class XcfaAasporLts( for (action in otherActions) { // for every action that is not in the source set it is checked whether it should be added to the source set // (because it is dependent with an action already in the source set) - val potentialIgnoredVars = mutableSetOf>() - if (sourceSet.any { areDependents(it, action, prec, potentialIgnoredVars) }) { - if (isBackwardAction(action)) { - return enabledActions.toSet() // see POR algorithm for the reason of removing backward transitions + val potentialIgnoredVars = mutableSetOf>() + if (sourceSet.any { areDependents(state, it, action, prec, potentialIgnoredVars) }) { + if (action.isBackward) { + return enabledActions.toSet() // see POR algorithm for the reason of handling backward edges this way } sourceSet.add(action) actionsToRemove.add(action) @@ -109,16 +115,19 @@ open class XcfaAasporLts( return sourceSet } - private fun areDependents(sourceSetAction: XcfaAction, action: XcfaAction, prec: Prec, - ignoredVariables: MutableSet>): Boolean { - if (isSameProcess(sourceSetAction, action)) { - return true - } - val usedBySourceSetAction = getCachedUsedSharedObjects(getEdgeOf(sourceSetAction)) - val influencedSharedObjects = getInfluencedSharedObjects(getEdgeOf(action)) - for (varDecl in influencedSharedObjects) { - if (usedBySourceSetAction.contains(varDecl)) { - if (varDecl !in prec.usedVars) { + private fun areDependents( + state: XcfaState>, + sourceSetAction: XcfaAction, action: XcfaAction, prec: Prec, + ignoredVariables: MutableSet> + ): Boolean { + if (sourceSetAction.pid == action.pid) return true + val sourceSetActionVars = getCachedUsedVars(getEdge(sourceSetAction)) + val influencedVars = getInfluencedVars(getEdge(action)) + + val precVars = prec.usedVars + for (varDecl in influencedVars) { + if (varDecl in sourceSetActionVars) { + if (varDecl !in precVars && varDecl !in fenceVars.values) { // the actions would be dependent, but we may have a chance to ignore it in the current abstraction ignoredVariables.add(varDecl) continue @@ -126,6 +135,6 @@ open class XcfaAasporLts( return true } } - return false + return indirectlyDependent(state, sourceSetAction, sourceSetActionVars, influencedVars) } } \ No newline at end of file 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 0700c06df6..cb9538822d 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 @@ -21,6 +21,7 @@ import hu.bme.mit.theta.analysis.Prec import hu.bme.mit.theta.analysis.State import hu.bme.mit.theta.analysis.algorithm.ArgNode import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.analysis.waitlist.Waitlist import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaState @@ -37,7 +38,7 @@ import kotlin.random.Random /** * Type definitions for states, actions and ARG nodes. */ -private typealias S = XcfaState +private typealias S = XcfaState> private typealias A = XcfaAction private typealias Node = ArgNode diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporCoiLts.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporCoiLts.kt new file mode 100644 index 0000000000..5cc7bb8fb1 --- /dev/null +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporCoiLts.kt @@ -0,0 +1,45 @@ +/* + * 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.por + +import hu.bme.mit.theta.analysis.LTS +import hu.bme.mit.theta.analysis.Prec +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.xcfa.analysis.XcfaAction +import hu.bme.mit.theta.xcfa.analysis.XcfaState +import hu.bme.mit.theta.xcfa.analysis.coi.transFuncVersion +import hu.bme.mit.theta.xcfa.model.XCFA +import hu.bme.mit.theta.xcfa.model.XcfaEdge + +class XcfaSporCoiLts( + xcfa: XCFA, + coiLTS: LTS>, XcfaAction> +) : XcfaSporLts(xcfa) { + + init { + simpleXcfaLts = coiLTS + } + + override fun

getEnabledActionsFor( + state: XcfaState>, exploredActions: Collection, prec: P + ): Set { + return getEnabledActionsFor(state, simpleXcfaLts.getEnabledActionsFor(state, exploredActions, prec)) + } + + override fun getEdge(action: XcfaAction): XcfaEdge = super.getEdge(action.transFuncVersion ?: action) +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporLts.kt b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporLts.kt index 654fc1fb3f..eff8485d1f 100644 --- a/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporLts.kt +++ b/subprojects/xcfa/xcfa-analysis/src/main/java/hu/bme/mit/theta/xcfa/analysis/por/XcfaSporLts.kt @@ -16,9 +16,20 @@ package hu.bme.mit.theta.xcfa.analysis.por import hu.bme.mit.theta.analysis.LTS -import hu.bme.mit.theta.core.decl.Decl +import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.expr.ExprState +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.Expr +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.BoolType +import hu.bme.mit.theta.core.utils.ExprUtils +import hu.bme.mit.theta.core.utils.PathUtils +import hu.bme.mit.theta.solver.Solver +import hu.bme.mit.theta.solver.utils.WithPushPop +import hu.bme.mit.theta.solver.z3.Z3SolverFactory import hu.bme.mit.theta.xcfa.* import hu.bme.mit.theta.xcfa.analysis.XcfaAction import hu.bme.mit.theta.xcfa.analysis.XcfaState @@ -36,10 +47,11 @@ import kotlin.random.Random * * @param xcfa the XCFA of the verified program */ -open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> { +open class XcfaSporLts(protected val xcfa: XCFA) : LTS>, XcfaAction> { companion object { + private val dependencySolver: Solver = Z3SolverFactory.getInstance().createSolver() var random: Random = Random.Default } @@ -48,23 +60,30 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> /* CACHE COLLECTIONS */ /** - * Shared objects (~global variables) used by a transition. + * Global variables used by an edge. */ - private val usedSharedObjects: MutableMap>> = mutableMapOf() + private val usedVars: MutableMap>> = mutableMapOf() /** - * Shared objects (~global variables) that are used by the key transition or by transitions reachable from the - * current state via a given transition. + * Global variables that are used by the key edge or by edges reachable from the + * current state via a given edge. */ - private val influencedSharedObjects: MutableMap>> = mutableMapOf() + private val influencedVars: MutableMap>> = mutableMapOf() /** - * Backward transitions in the transition system (a transition of a loop). + * Backward edges in the CFA (an edge of a loop). */ - protected val backwardTransitions: MutableSet = mutableSetOf() + private val backwardEdges: MutableSet> = mutableSetOf() + + /** + * Variables associated to mutex identifiers. TODO: this should really be solved by storing VarDecls in FenceLabel. + */ + protected val fenceVars: MutableMap> = mutableMapOf() + private val String.fenceVar + get() = fenceVars.getOrPut("") { Decls.Var(if (this == "") "__THETA_atomic_mutex_" else this, Bool()) } init { - collectBackwardTransitions() + collectBackwardEdges() } /** @@ -73,15 +92,19 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param state the state whose enabled actions we would like to know * @return the enabled actions */ - override fun getEnabledActionsFor(state: XcfaState<*>): Set { - // Collecting enabled actions - val allEnabledActions = simpleXcfaLts.getEnabledActionsFor(state) + override fun getEnabledActionsFor(state: XcfaState>): Set = + getEnabledActionsFor(state, simpleXcfaLts.getEnabledActionsFor(state)) - // Calculating the source set starting from every (or some of the) enabled transition; the minimal source set is stored + /** + * Calculates the source set starting from every (or some of the) enabled transition; the minimal source set is returned. + */ + protected open fun getEnabledActionsFor( + state: XcfaState>, allEnabledActions: Collection + ): Set { var minimalSourceSet = setOf() val sourceSetFirstActions = getSourceSetFirstActions(state, allEnabledActions) for (firstActions in sourceSetFirstActions) { - val sourceSet = calculateSourceSet(allEnabledActions, firstActions) + val sourceSet = calculateSourceSet(state, allEnabledActions, firstActions) if (minimalSourceSet.isEmpty() || sourceSet.size < minimalSourceSet.size) { minimalSourceSet = sourceSet } @@ -95,8 +118,10 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param allEnabledActions the enabled actions in the present state * @return the possible starting actions of a source set */ - protected fun getSourceSetFirstActions(state: XcfaState<*>, - allEnabledActions: Collection): Collection> { + protected fun getSourceSetFirstActions( + state: XcfaState>, + allEnabledActions: Collection + ): Collection> { val enabledActionsByProcess = allEnabledActions.groupBy(XcfaAction::pid) val enabledProcesses = enabledActionsByProcess.keys.toList().shuffled(random) return enabledProcesses.map { pid -> @@ -116,8 +141,10 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param enabledActionsByProcess the enabled actions grouped by processes * @return the set of first processes */ - private fun checkMutexBlocks(state: XcfaState<*>, pid: Int, firstProcesses: MutableSet, - enabledActionsByProcess: Map>) { + private fun checkMutexBlocks( + state: XcfaState>, pid: Int, firstProcesses: MutableSet, + enabledActionsByProcess: Map> + ) { val processState = checkNotNull(state.processes[pid]) if (!processState.paramsInitialized) return val disabledOutEdges = processState.locs.peek().outgoingEdges.filter { edge -> @@ -145,13 +172,17 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param firstActions the actions that will be added to the source set as the first actions * @return a source set of enabled actions */ - private fun calculateSourceSet(enabledActions: Collection, - firstActions: Collection): Set { - if (firstActions.any(::isBackwardAction)) { + private fun calculateSourceSet( + state: XcfaState>, + enabledActions: Collection, + firstActions: Collection + ): Set { + if (firstActions.any { it.isBackward }) { return enabledActions.toSet() } val sourceSet = firstActions.toMutableSet() - val otherActions = (enabledActions.toMutableSet() subtract sourceSet).toMutableSet() // actions not in the source set + val otherActions = + (enabledActions.toMutableSet() subtract sourceSet).toMutableSet() // actions not in the source set var addedNewAction = true while (addedNewAction) { addedNewAction = false @@ -159,9 +190,9 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> for (action in otherActions) { // for every action that is not in the source set it is checked whether it should be added to the source set // (because it is dependent with an action already in the source set) - if (sourceSet.any { areDependents(it, action) }) { - if (isBackwardAction(action)) { - return enabledActions.toSet() // see POR algorithm for the reason of removing backward transitions + if (sourceSet.any { dependent(state, it, action) }) { + if (action.isBackward) { + return enabledActions.toSet() // see POR algorithm for the reason of handling backward edges this way } sourceSet.add(action) actionsToRemove.add(action) @@ -181,98 +212,113 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param action the other action (not in the source set) * @return true, if the two actions are dependent in the context of source sets */ - private fun areDependents(sourceSetAction: XcfaAction, action: XcfaAction): Boolean { - val usedBySourceSetAction = getCachedUsedSharedObjects(getEdgeOf(sourceSetAction)) - return isSameProcess(sourceSetAction, action) || - getInfluencedSharedObjects(getEdgeOf(action)).any { it in usedBySourceSetAction } + private fun dependent( + state: XcfaState>, sourceSetAction: XcfaAction, action: XcfaAction + ): Boolean { + if (sourceSetAction.pid == action.pid) return true + + val sourceSetActionVars = getCachedUsedVars(getEdge(sourceSetAction)) + val influencedVars = getInfluencedVars(getEdge(action)) + if ((influencedVars intersect sourceSetActionVars).isNotEmpty()) return true + + return indirectlyDependent(state, sourceSetAction, sourceSetActionVars, influencedVars) } - /** - * Determines whether two actions are in the same process. - * - * @param action1 the first action - * @param action2 the second action - * @return true, if the two actions are in the same process - */ - protected fun isSameProcess(action1: XcfaAction, action2: XcfaAction) = action1.pid == action2.pid + protected fun indirectlyDependent( + state: XcfaState>, sourceSetAction: XcfaAction, + sourceSetActionVars: Set>, influencedVars: Set> + ): Boolean { + val sourceSetActionMemLocs = sourceSetActionVars.pointsTo(xcfa) + val influencedMemLocs = influencedVars.pointsTo(xcfa) + val intersection = sourceSetActionMemLocs intersect influencedMemLocs + if (intersection.isEmpty()) return false // they cannot point to the same memory location even based on static info + + val derefs = sourceSetAction.label.dereferences.map { it.array } + var expr: Expr = Or(intersection.flatMap { memLoc -> derefs.map { Eq(memLoc, it) } }) + expr = (state.sGlobal.innerState as? ExplState)?.let { s -> + ExprUtils.simplify(expr, s.`val`) + } ?: ExprUtils.simplify(expr) + if (expr == True()) return true + return WithPushPop(dependencySolver).use { + dependencySolver.add(PathUtils.unfold(state.sGlobal.toExpr(), 0)) + dependencySolver.add( + PathUtils.unfold(expr, 0) + ) // is it always given that the state will produce 0 indexed constants? + dependencySolver.check().isSat // two pointers may point to the same memory location + } + } /** * Returns the global variables that an edge uses (it is present in one of its labels). + * Mutex variables are also considered to avoid running into a deadlock and stop exploration. * * @param edge whose global variables are to be returned * @return the set of used global variables */ - private fun getDirectlyUsedSharedObjects(edge: XcfaEdge): Set> { + private fun getDirectlyUsedVars(edge: XcfaEdge): Set> { val globalVars = xcfa.vars.map(XcfaGlobalVar::wrappedVar) return edge.getFlatLabels().flatMap { label -> - label.collectVars().filter { it in globalVars } - }.toSet() + label.collectVars().filter { it in globalVars } union + ((label as? FenceLabel)?.labels + ?.filter { it.startsWith("start_cond_wait") || it.startsWith("cond_signal") } + ?.map { it.substringAfter("(").substringBefore(")").split(",")[0] } + ?.map { it.fenceVar } ?: listOf()) + }.toSet() union edge.acquiredEmbeddedFenceVars.let { mutexes -> + if (mutexes.size <= 1) setOf() else mutexes.map { it.fenceVar } + } } /** * Returns the global variables that an edge uses or if it is the start of an atomic block the global variables - * that are used in the atomic block. + * that are used in the atomic block. The result is cached. * * @param edge whose global variables are to be returned * @return the set of directly or indirectly used global variables */ - private fun getUsedSharedObjects(edge: XcfaEdge): Set> { + protected fun getCachedUsedVars(edge: XcfaEdge): Set> { + if (edge in usedVars) return usedVars[edge]!! val flatLabels = edge.getFlatLabels() val mutexes = flatLabels.filterIsInstance().flatMap { it.acquiredMutexes }.toMutableSet() - return if (mutexes.isEmpty()) { - getDirectlyUsedSharedObjects(edge) + val vars = if (mutexes.isEmpty()) { + getDirectlyUsedVars(edge) } else { - getSharedObjectsWithBFS(edge) { it.mutexOperations(mutexes) }.toSet() + getVarsWithBFS(edge) { it.mutexOperations(mutexes) }.toSet() } + usedVars[edge] = vars + return vars } /** - * Same as [getUsedSharedObjects] with an additional cache layer. - * - * @param edge whose shared objects are to be returned - * @return the set of directly or indirectly used shared objects - */ - protected fun getCachedUsedSharedObjects(edge: XcfaEdge): Set> { - if (!usedSharedObjects.containsKey(edge)) { - val vars = getUsedSharedObjects(edge) - usedSharedObjects[edge] = vars - } - return usedSharedObjects[edge]!! - } - - /** - * Returns the shared objects (~global variables) used by the given transition or by transitions that are reachable - * via the given transition ("influenced shared objects"). + * Returns the global variables used by the given edge or by edges that are reachable + * via the given edge ("influenced vars"). * - * @param edge whose successor transitions' shared objects are to be returned. - * @return the set of influenced shared objects + * @param edge whose successor edges' global variables are to be returned. + * @return the set of influenced global variables */ - protected fun getInfluencedSharedObjects(edge: XcfaEdge): Set> { - if (!influencedSharedObjects.containsKey(edge)) { - influencedSharedObjects[edge] = getSharedObjectsWithBFS(edge) { true } - } - return influencedSharedObjects[edge]!! + protected fun getInfluencedVars(edge: XcfaEdge): Set> { + if (edge in influencedVars) return influencedVars[edge]!! + val vars = getVarsWithBFS(edge) { true } + influencedVars[edge] = vars + return vars } /** - * Returns shared objects (~global variables) encountered in a search starting from a given transition. + * Returns global variables encountered in a search starting from a given edge. * - * @param startTransition the start point (transition) of the search - * @param goFurther the predicate that tells whether more transitions have to be explored through this - * transition - * @return the set of encountered shared objects + * @param startEdge the start point of the search + * @param goFurther the predicate that tells whether more edges have to be explored through this edge + * @return the set of encountered global variables */ - private fun getSharedObjectsWithBFS(startTransition: XcfaEdge, - goFurther: Predicate): Set> { - val vars = mutableSetOf>() + private fun getVarsWithBFS(startEdge: XcfaEdge, goFurther: Predicate): Set> { + val vars = mutableSetOf>() val exploredEdges = mutableListOf() val edgesToExplore = mutableListOf() - edgesToExplore.add(startTransition) + edgesToExplore.add(startEdge) while (edgesToExplore.isNotEmpty()) { val exploring = edgesToExplore.removeFirst() - vars.addAll(getDirectlyUsedSharedObjects(exploring)) + vars.addAll(getDirectlyUsedVars(exploring)) if (goFurther.test(exploring)) { - val successiveEdges = getSuccessiveTransitions(exploring) + val successiveEdges = getSuccessiveEdges(exploring) for (newEdge in successiveEdges) { if (newEdge !in exploredEdges) { edgesToExplore.add(newEdge) @@ -290,15 +336,16 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> * @param action the action whose edge is to be returned * @return the edge of the action */ - protected open fun getEdgeOf(action: XcfaAction) = action.edge + protected open fun getEdge(action: XcfaAction) = action.edge /** - * Returns the outgoing edges of the target of the given edge. + * Returns the outgoing edges of the target of the given edge. For start threads, the first edges of the started + * procedures are also included. * * @param edge the edge whose target's outgoing edges are to be returned * @return the outgoing edges of the target of the edge */ - private fun getSuccessiveTransitions(edge: XcfaEdge): Set { + private fun getSuccessiveEdges(edge: XcfaEdge): Set { val outgoingEdges = edge.target.outgoingEdges.toMutableSet() val startThreads = edge.getFlatLabels().filterIsInstance().toList() if (startThreads.isNotEmpty()) { // for start thread labels, the thread procedure must be explored, too! @@ -310,17 +357,16 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> } /** - * Determines whether the given action is a backward action. + * Determines whether this action is a backward action. * - * @param action the action to be classified as backward action or non-backward action * @return true, if the action is a backward action */ - protected open fun isBackwardAction(action: XcfaAction): Boolean = backwardTransitions.contains(getEdgeOf(action)) + protected open val XcfaAction.isBackward: Boolean get() = backwardEdges.any { it.first == source && it.second == target } /** * Collects backward edges of the given XCFA. */ - private fun collectBackwardTransitions() { + private fun collectBackwardEdges() { for (procedure in xcfa.procedures) { // DFS for every procedure of the XCFA to discover backward edges val visitedLocations = mutableSetOf() @@ -332,8 +378,8 @@ open class XcfaSporLts(protected val xcfa: XCFA) : LTS, XcfaAction> visitedLocations.add(visiting) for (outgoingEdge in visiting.outgoingEdges) { val target = outgoingEdge.target - if (visitedLocations.contains(target)) { // backward edge - backwardTransitions.add(outgoingEdge) + if (target in visitedLocations) { // backward edge + backwardEdges.add(outgoingEdge.source to outgoingEdge.target) } else { stack.push(target) } diff --git a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt index 47c477cd19..185e409e21 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt +++ b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaExplAnalysisTest.kt @@ -24,6 +24,10 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions import hu.bme.mit.theta.analysis.expl.* import hu.bme.mit.theta.analysis.expr.refinement.* +import hu.bme.mit.theta.analysis.ptr.ItpRefToPtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.analysis.ptr.getPtrPartialOrd import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist import hu.bme.mit.theta.c2xcfa.getXcfaFromC import hu.bme.mit.theta.common.logging.ConsoleLogger @@ -73,7 +77,7 @@ class XcfaExplAnalysisTest { xcfa, Z3LegacySolverFactory.getInstance().createSolver(), 1, - getPartialOrder(ExplOrd.getInstance()) + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false ) val lts = getXcfaLts() @@ -81,24 +85,25 @@ class XcfaExplAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, ExplPrec, ItpRefutation>(ItpRefToExplPrec()) + val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec())) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(ExplPrec.empty())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) @@ -117,7 +122,7 @@ class XcfaExplAnalysisTest { xcfa, Z3LegacySolverFactory.getInstance().createSolver(), 1, - getPartialOrder(ExplOrd.getInstance()) + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false ) val lts = XcfaSporLts(xcfa) @@ -125,24 +130,25 @@ class XcfaExplAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, ExplPrec, ItpRefutation>(ItpRefToExplPrec()) + val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec())) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(ExplPrec.empty())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) @@ -162,31 +168,33 @@ class XcfaExplAnalysisTest { xcfa, Z3LegacySolverFactory.getInstance().createSolver(), 1, - XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance())) + XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), false ) val lts = XcfaDporLts(xcfa) val abstractor = getXcfaAbstractor(analysis, lts.waitlist, - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, ExplPrec, ItpRefutation>(ItpRefToExplPrec()) + val precRefiner = XcfaPrecRefiner>, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec())) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - ConsoleLogger(Logger.Level.DETAIL)) as Refiner, XcfaAction, XcfaPrec> + ConsoleLogger( + Logger.Level.DETAIL)) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(ExplPrec.empty())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) @@ -205,7 +213,7 @@ class XcfaExplAnalysisTest { xcfa, Z3LegacySolverFactory.getInstance().createSolver(), 1, - getPartialOrder(ExplOrd.getInstance()) + getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd()), false ) val lts = XcfaAasporLts(xcfa, mutableMapOf()) @@ -213,25 +221,26 @@ class XcfaExplAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner(ItpRefToExplPrec()) - val atomicNodePruner = AtomicNodePruner, XcfaAction>() + val precRefiner = XcfaPrecRefiner, ExplPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToExplPrec())) + val atomicNodePruner = AtomicNodePruner>, XcfaAction>() val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, NullLogger.getInstance(), - atomicNodePruner) as Refiner, XcfaAction, XcfaPrec> + atomicNodePruner) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf())) - val safetyResult = cegarChecker.check(XcfaPrec(ExplPrec.empty())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) @@ -251,31 +260,31 @@ class XcfaExplAnalysisTest { xcfa, Z3LegacySolverFactory.getInstance().createSolver(), 1, - XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance())) + XcfaDporLts.getPartialOrder(getPartialOrder(ExplOrd.getInstance().getPtrPartialOrd())), false ) val lts = XcfaAadporLts(xcfa) val abstractor = getXcfaAbstractor(analysis, lts.waitlist, - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner(ItpRefToExplPrec()) + val precRefiner = XcfaPrecRefiner(ItpRefToPtrPrec(ItpRefToExplPrec())) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(ExplPrec.empty())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet()))) diff --git a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt index 1472d61e6b..3ef10bf544 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt +++ b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaPredAnalysisTest.kt @@ -24,6 +24,10 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner import hu.bme.mit.theta.analysis.algorithm.cegar.abstractor.StopCriterions import hu.bme.mit.theta.analysis.expr.refinement.* import hu.bme.mit.theta.analysis.pred.* +import hu.bme.mit.theta.analysis.ptr.ItpRefToPtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.analysis.ptr.getPtrPartialOrd import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist import hu.bme.mit.theta.c2xcfa.getXcfaFromC import hu.bme.mit.theta.common.logging.ConsoleLogger @@ -74,7 +78,8 @@ class XcfaPredAnalysisTest { xcfa, solver, PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver)) + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), + false ) val lts = getXcfaLts() @@ -82,25 +87,25 @@ class XcfaPredAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, PredPrec, ItpRefutation>( - ItpRefToPredPrec(ExprSplitters.whole())) + val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(PredPrec.of())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) @@ -120,7 +125,7 @@ class XcfaPredAnalysisTest { xcfa, solver, PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver)) + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), false ) val lts = XcfaSporLts(xcfa) @@ -128,25 +133,25 @@ class XcfaPredAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, PredPrec, ItpRefutation>( - ItpRefToPredPrec(ExprSplitters.whole())) + val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(PredPrec.of())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) @@ -167,32 +172,33 @@ class XcfaPredAnalysisTest { xcfa, solver, PredAbstractors.cartesianAbstractor(solver), - XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver))) + XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), false ) val lts = XcfaDporLts(xcfa) val abstractor = getXcfaAbstractor(analysis, lts.waitlist, - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner, PredPrec, ItpRefutation>( - ItpRefToPredPrec(ExprSplitters.whole())) + val precRefiner = XcfaPrecRefiner>, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - ConsoleLogger(Logger.Level.DETAIL)) as Refiner, XcfaAction, XcfaPrec> + ConsoleLogger( + Logger.Level.DETAIL)) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(PredPrec.of())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) @@ -212,7 +218,7 @@ class XcfaPredAnalysisTest { xcfa, solver, PredAbstractors.cartesianAbstractor(solver), - getPartialOrder(PredOrd.create(solver)) + getPartialOrder(PredOrd.create(solver).getPtrPartialOrd()), false ) val lts = XcfaAasporLts(xcfa, mutableMapOf()) @@ -220,25 +226,26 @@ class XcfaPredAnalysisTest { val abstractor = getXcfaAbstractor(analysis, PriorityWaitlist.create( ArgNodeComparators.combine(ArgNodeComparators.targetFirst(), ArgNodeComparators.bfs())), - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner(ItpRefToPredPrec(ExprSplitters.whole())) - val atomicNodePruner = AtomicNodePruner, XcfaAction>() + val precRefiner = XcfaPrecRefiner, PredPrec, ItpRefutation>( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) + val atomicNodePruner = AtomicNodePruner>, XcfaAction>() val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, NullLogger.getInstance(), - atomicNodePruner) as Refiner, XcfaAction, XcfaPrec> + atomicNodePruner) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, AasporRefiner.create(refiner, PruneStrategy.FULL, mutableMapOf())) - val safetyResult = cegarChecker.check(XcfaPrec(PredPrec.of())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) @@ -259,31 +266,32 @@ class XcfaPredAnalysisTest { xcfa, solver, PredAbstractors.cartesianAbstractor(solver), - XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver))) + XcfaDporLts.getPartialOrder(getPartialOrder(PredOrd.create(solver).getPtrPartialOrd())), false ) val lts = XcfaAadporLts(xcfa) val abstractor = getXcfaAbstractor(analysis, lts.waitlist, - StopCriterions.firstCex, XcfaAction>(), + StopCriterions.firstCex>, XcfaAction>(), ConsoleLogger(Logger.Level.DETAIL), lts, - ErrorDetection.ERROR_LOCATION) as Abstractor, XcfaAction, XcfaPrec> + ErrorDetection.ERROR_LOCATION) as Abstractor>, XcfaAction, XcfaPrec>> - val precRefiner = XcfaPrecRefiner(ItpRefToPredPrec(ExprSplitters.whole())) + val precRefiner = XcfaPrecRefiner( + ItpRefToPtrPrec(ItpRefToPredPrec(ExprSplitters.whole()))) val refiner = XcfaSingleExprTraceRefiner.create( ExprTraceBwBinItpChecker.create(BoolExprs.True(), BoolExprs.True(), Z3LegacySolverFactory.getInstance().createItpSolver()), precRefiner, PruneStrategy.FULL, - NullLogger.getInstance()) as Refiner, XcfaAction, XcfaPrec> + NullLogger.getInstance()) as Refiner>, XcfaAction, XcfaPrec>> val cegarChecker = CegarChecker.create(abstractor, refiner) - val safetyResult = cegarChecker.check(XcfaPrec(PredPrec.of())) + val safetyResult = cegarChecker.check(XcfaPrec(PtrPrec(PredPrec.of(), emptySet()))) diff --git a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateLtsTest.kt b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateLtsTest.kt index f9997d02d2..b1a815104b 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateLtsTest.kt +++ b/subprojects/xcfa/xcfa-analysis/src/test/java/hu/bme/mit/theta/xcfa/analysis/XcfaStateLtsTest.kt @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.analysis import hu.bme.mit.theta.analysis.expl.ExplPrec import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.core.type.inttype.IntExprs import hu.bme.mit.theta.xcfa.analysis.XcfaProcessState.Companion.createLookup import hu.bme.mit.theta.xcfa.analysis.por.XcfaAasporLts @@ -32,10 +33,10 @@ class XcfaStateLtsTest { @Test fun testApply() { - val actionOrder: MutableList<(XcfaState) -> XcfaAction> = ArrayList() - val expectations: MutableList>> = ArrayList() + val actionOrder: MutableList<(XcfaState>) -> XcfaAction> = ArrayList() + val expectations: MutableList>>> = ArrayList() val lts = getXcfaLts() - lateinit var initState: XcfaState + lateinit var initState: XcfaState> lateinit var xcfa: XCFA val edges: MutableList = ArrayList() @@ -73,7 +74,7 @@ class XcfaStateLtsTest { ) ) ), - ExplState.bottom() + PtrState(ExplState.bottom()) ) val sporLts = XcfaSporLts(xcfa) val aasporLts = XcfaAasporLts(xcfa, LinkedHashMap()) diff --git a/subprojects/xcfa/xcfa-analysis/src/test/resources/04multithread.c b/subprojects/xcfa/xcfa-analysis/src/test/resources/04multithread.c index cf009eb495..9acbdb6de5 100644 --- a/subprojects/xcfa/xcfa-analysis/src/test/resources/04multithread.c +++ b/subprojects/xcfa/xcfa-analysis/src/test/resources/04multithread.c @@ -686,6 +686,9 @@ extern int pthread_atfork (void (*__prepare) (void), void reach_error(){} +void __VERIFIER_atomic_begin(){} +void __VERIFIER_atomic_end(){} + int x = 0; int ERR = 0; 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 e7725c1df2..46ad6180f8 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 @@ -25,10 +25,14 @@ import hu.bme.mit.theta.analysis.Trace import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.algorithm.debug.ARGWebDebugger import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.analysis.utils.ArgVisualizer import hu.bme.mit.theta.analysis.utils.TraceVisualizer +import hu.bme.mit.theta.c2xcfa.CMetaData import hu.bme.mit.theta.cat.dsl.CatDslManager import hu.bme.mit.theta.common.logging.Logger +import hu.bme.mit.theta.common.logging.Logger.Level.INFO +import hu.bme.mit.theta.common.logging.Logger.Level.RESULT import hu.bme.mit.theta.common.visualization.Graph import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter import hu.bme.mit.theta.common.visualization.writer.WebDebuggerLogger @@ -46,16 +50,23 @@ import hu.bme.mit.theta.xcfa.cli.checkers.getChecker import hu.bme.mit.theta.xcfa.cli.params.* import hu.bme.mit.theta.xcfa.cli.utils.* import hu.bme.mit.theta.xcfa.cli.witnesses.XcfaTraceConcretizer +import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.XCFA +import hu.bme.mit.theta.xcfa.model.XcfaLabel import hu.bme.mit.theta.xcfa.model.toDot +import hu.bme.mit.theta.xcfa.passes.FetchExecuteWriteback import hu.bme.mit.theta.xcfa.passes.LbePass import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass +import hu.bme.mit.theta.xcfa.passes.StaticCoiPass import hu.bme.mit.theta.xcfa.toC import java.io.File import java.util.concurrent.TimeUnit import kotlin.random.Random -fun runConfig(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): SafetyResult<*, *> { +fun runConfig( + config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger, + throwDontExit: Boolean +): SafetyResult<*, *> { propagateInputOptions(config, logger, uniqueLogger) registerAllSolverManagers(config.backendConfig.solverHome, logger) @@ -66,7 +77,7 @@ fun runConfig(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): S preVerificationLogging(xcfa, mcm, parseContext, config, logger, uniqueLogger) - val result = backend(xcfa, mcm, parseContext, config, logger, uniqueLogger) + val result = backend(xcfa, mcm, parseContext, config, logger, uniqueLogger, throwDontExit) postVerificationLogging(result, mcm, parseContext, config, logger, uniqueLogger) @@ -77,6 +88,7 @@ fun runConfig(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): S private fun propagateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) { config.inputConfig.property = determineProperty(config, logger) LbePass.level = config.frontendConfig.lbeLevel + StaticCoiPass.enabled = config.frontendConfig.staticCoi if (config.backendConfig.backend == Backend.CEGAR) { val cegarConfig = config.backendConfig.specConfig cegarConfig as CegarConfig @@ -90,6 +102,8 @@ private fun propagateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniq } LoopUnrollPass.UNROLL_LIMIT = config.frontendConfig.loopUnroll + LoopUnrollPass.FORCE_UNROLL_LIMIT = config.frontendConfig.forceUnroll + FetchExecuteWriteback.enabled = config.frontendConfig.enableFew ARGWebDebugger.on = config.debugConfig.argdebug } @@ -107,7 +121,12 @@ private fun validateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqu (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isDynamic == true && (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.search != Search.DFS } - // TODO add more validation options + rule("SensibleLoopUnrollLimits") { + config.frontendConfig.loopUnroll != -1 && config.frontendConfig.loopUnroll < config.frontendConfig.forceUnroll + } + rule("NoPredSplitUntilFixed(https://github.com/ftsrg/theta/issues/267)") { + (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.domain == Domain.PRED_SPLIT + } } fun frontend(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): Triple { @@ -132,9 +151,11 @@ fun frontend(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): Tr val cConfig = config.frontendConfig.specConfig cConfig as CFrontendConfig parseContext.arithmetic = cConfig.arithmetic + parseContext.architecture = cConfig.architecture } val xcfa = getXcfa(config, parseContext, logger, uniqueLogger) + val mcm = if (config.inputConfig.catFile != null) { CatDslManager.createMCM(config.inputConfig.catFile!!) } else { @@ -143,114 +164,206 @@ fun frontend(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): Tr ConeOfInfluence = if (parseContext.multiThreading) XcfaCoiMultiThread(xcfa) else XcfaCoiSingleThread(xcfa) - logger.write(Logger.Level.INFO, "Frontend finished: ${xcfa.name} (in ${ + if (parseContext.multiThreading && (config.backendConfig.specConfig as? CegarConfig)?.let { it.abstractorConfig.search == Search.ERR } == true) { + val cConfig = config.backendConfig.specConfig as CegarConfig + cConfig.abstractorConfig.search = Search.DFS + uniqueLogger.write(INFO, "Multithreaded program found, using DFS instead of ERR.") + } + + logger.write( + Logger.Level.INFO, "Frontend finished: ${xcfa.name} (in ${ stopwatch.elapsed(TimeUnit.MILLISECONDS) - } ms)\n") + } ms)\n" + ) - logger.write(Logger.Level.RESULT, "ParsingResult Success\n") + logger.write(RESULT, "ParsingResult Success\n") + logger.write(RESULT, + "Alias graph size: ${xcfa.pointsToGraph.size} -> ${xcfa.pointsToGraph.values.map { it.size }.toList()}\n") return Triple(xcfa, mcm, parseContext) } -private fun backend(xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, +private fun backend( + xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, logger: Logger, - uniqueLogger: Logger): SafetyResult<*, *> = + uniqueLogger: Logger, + throwDontExit: Boolean +): SafetyResult<*, *> = if (config.backendConfig.backend == Backend.NONE) { SafetyResult.unknown() } else { if (xcfa.procedures.all { it.errorLoc.isEmpty && config.inputConfig.property == ErrorDetection.ERROR_LOCATION }) { - SafetyResult.safe() + val result = SafetyResult.safe() + logger.write(Logger.Level.INFO, "Input is trivially safe\n") + + logger.write(RESULT, result.toString() + "\n") + result } else { val stopwatch = Stopwatch.createStarted() - logger.write(Logger.Level.INFO, - "Starting verification of ${if (xcfa.name == "") "UnnamedXcfa" else xcfa.name} using ${config.backendConfig.backend}\n") + logger.write( + Logger.Level.INFO, + "Starting verification of ${if (xcfa.name == "") "UnnamedXcfa" else xcfa.name} using ${config.backendConfig.backend}\n" + ) val checker = getChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger) - val result = exitOnError(config.debugConfig.stacktrace, config.debugConfig.debug) { + val result = exitOnError(config.debugConfig.stacktrace, config.debugConfig.debug || throwDontExit) { checker.check() + }.let { result -> + when { + 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() + } + + else -> result + } } - logger.write(Logger.Level.INFO, "Backend finished (in ${ + logger.write( + Logger.Level.INFO, "Backend finished (in ${ stopwatch.elapsed(TimeUnit.MILLISECONDS) - } ms)\n") + } ms)\n" + ) - logger.write(Logger.Level.RESULT, result.toString() + "\n") + logger.write(RESULT, result.toString() + "\n") result } } -private fun preVerificationLogging(xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, - logger: Logger, - uniqueLogger: Logger) { - try { - val resultFolder = config.outputConfig.resultFolder - resultFolder.mkdirs() - - logger.write(Logger.Level.INFO, - "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n") - - if (!config.outputConfig.xcfaOutputConfig.disable) { - val xcfaDotFile = File(resultFolder, "xcfa.dot") - xcfaDotFile.writeText(xcfa.toDot()) - - val xcfaJsonFile = File(resultFolder, "xcfa.json") - val uglyJson = getGson(xcfa).toJson(xcfa) - val create = GsonBuilder().setPrettyPrinting().create() - xcfaJsonFile.writeText(create.toJson(JsonParser.parseString(uglyJson))) - } +private fun preVerificationLogging( + xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>, + logger: Logger, uniqueLogger: Logger +) { + if (config.outputConfig.enableOutput) { + try { + val resultFolder = config.outputConfig.resultFolder + resultFolder.mkdirs() + + logger.write( + Logger.Level.INFO, + "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n" + ) + + if (!config.outputConfig.xcfaOutputConfig.disable) { + val xcfaDotFile = File(resultFolder, "xcfa.dot") + xcfaDotFile.writeText(xcfa.toDot()) + + val xcfaJsonFile = File(resultFolder, "xcfa.json") + val uglyJson = getGson(xcfa).toJson(xcfa) + val create = GsonBuilder().setPrettyPrinting().create() + xcfaJsonFile.writeText(create.toJson(JsonParser.parseString(uglyJson))) + } - if (!config.outputConfig.cOutputConfig.disable) { - try { - val xcfaCFile = File(resultFolder, "xcfa.c") - xcfaCFile.writeText(xcfa.toC(parseContext, config.outputConfig.cOutputConfig.useArr, - config.outputConfig.cOutputConfig.useExArr, config.outputConfig.cOutputConfig.useRange)) - } catch (e: Throwable) { - logger.write(Logger.Level.VERBOSE, "Could not emit C file\n") + if (!config.outputConfig.cOutputConfig.disable) { + try { + val xcfaCFile = File(resultFolder, "xcfa.c") + xcfaCFile.writeText( + xcfa.toC( + parseContext, config.outputConfig.cOutputConfig.useArr, + config.outputConfig.cOutputConfig.useExArr, config.outputConfig.cOutputConfig.useRange + ) + ) + } catch (e: Throwable) { + logger.write(Logger.Level.VERBOSE, "Could not emit C file\n") + } } + } catch (e: Throwable) { + logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } - } catch (e: Throwable) { - logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } } -private fun postVerificationLogging(safetyResult: SafetyResult<*, *>, mcm: MCM, - parseContext: ParseContext, config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) { - try { - // we only want to log the files if the current configuration is not --in-process or portfolio - if (config.backendConfig.inProcess || config.backendConfig.backend == Backend.PORTFOLIO) { - return - } +private fun postVerificationLogging( + safetyResult: SafetyResult<*, *>, mcm: MCM, + parseContext: ParseContext, config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger +) { + if (config.outputConfig.enableOutput) { + try { + // we only want to log the files if the current configuration is not --in-process or portfolio + if (config.backendConfig.inProcess || config.backendConfig.backend == Backend.PORTFOLIO) { + return + } - val resultFolder = config.outputConfig.resultFolder - resultFolder.mkdirs() + val resultFolder = config.outputConfig.resultFolder + resultFolder.mkdirs() - logger.write(Logger.Level.INFO, - "Writing post-verification artifacts to directory ${resultFolder.absolutePath}\n") + logger.write( + Logger.Level.INFO, + "Writing post-verification artifacts to directory ${resultFolder.absolutePath}\n" + ) - if (!config.outputConfig.argConfig.disable && safetyResult.hasArg()) { - val argFile = File(resultFolder, "arg-${safetyResult.isSafe}.dot") - val g: Graph = ArgVisualizer.getDefault().visualize(safetyResult.arg) - argFile.writeText(GraphvizWriter.getInstance().writeString(g)) - } - - if (!config.outputConfig.witnessConfig.disable) { - if (safetyResult.isUnsafe && safetyResult.asUnsafe().trace != null) { - val concrTrace: Trace, XcfaAction> = XcfaTraceConcretizer.concretize( - safetyResult.asUnsafe().trace as Trace, XcfaAction>?, - getSolver(config.outputConfig.witnessConfig.concretizerSolver, - config.outputConfig.witnessConfig.validateConcretizerSolver)) + if (!config.outputConfig.argConfig.disable && safetyResult.hasArg()) { + val argFile = File(resultFolder, "arg-${safetyResult.isSafe}.dot") + val g: Graph = ArgVisualizer.getDefault().visualize(safetyResult.arg) + argFile.writeText(GraphvizWriter.getInstance().writeString(g)) + } - val traceFile = File(resultFolder, "trace.dot") - val traceG: Graph = TraceVisualizer.getDefault().visualize(concrTrace) - traceFile.writeText(GraphvizWriter.getInstance().writeString(traceG)) + if (!config.outputConfig.witnessConfig.disable) { + if (safetyResult.isUnsafe && safetyResult.asUnsafe().trace != null) { + val concrTrace: Trace, XcfaAction> = XcfaTraceConcretizer.concretize( + safetyResult.asUnsafe().trace as Trace>, XcfaAction>?, + getSolver( + config.outputConfig.witnessConfig.concretizerSolver, + config.outputConfig.witnessConfig.validateConcretizerSolver + ), + parseContext + ) + + val traceFile = File(resultFolder, "trace.dot") + val traceG: Graph = TraceVisualizer.getDefault().visualize(concrTrace) + traceFile.writeText(GraphvizWriter.getInstance().writeString(traceG)) + + val sequenceFile = File(resultFolder, "trace.plantuml") + writeSequenceTrace(sequenceFile, + safetyResult.asUnsafe().trace as Trace, XcfaAction>) { (_, act) -> + act.label.getFlatLabels().map(XcfaLabel::toString) + } + + val optSequenceFile = File(resultFolder, "trace-optimized.plantuml") + writeSequenceTrace(optSequenceFile, concrTrace) { (_, act) -> + act.label.getFlatLabels().map(XcfaLabel::toString) + } + + val cSequenceFile = File(resultFolder, "trace-c.plantuml") + writeSequenceTrace(cSequenceFile, concrTrace) { (state, act) -> + val proc = state.processes[act.pid] + val loc = proc?.locs?.peek() + (loc?.metadata as? CMetaData)?.sourceText?.split("\n") ?: listOf("") + } + } + val witnessFile = File(resultFolder, "witness.graphml") + XcfaWitnessWriter().writeWitness( + safetyResult, config.inputConfig.input!!, + getSolver( + config.outputConfig.witnessConfig.concretizerSolver, + config.outputConfig.witnessConfig.validateConcretizerSolver + ), parseContext, witnessFile + ) } - val witnessFile = File(resultFolder, "witness.graphml") - XcfaWitnessWriter().writeWitness(safetyResult, config.inputConfig.input!!, - getSolver(config.outputConfig.witnessConfig.concretizerSolver, - config.outputConfig.witnessConfig.validateConcretizerSolver), parseContext, witnessFile) + } catch (e: Throwable) { + logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } - } catch (e: Throwable) { - logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n") } +} + +private fun writeSequenceTrace(sequenceFile: File, trace: Trace, XcfaAction>, + printer: (Pair, XcfaAction>) -> List) { + sequenceFile.writeText("@startuml\n") + var maxWidth = 0 + trace.actions.forEachIndexed { i, it -> + val stateBefore = trace.states[i] + sequenceFile.appendText("hnote over ${it.pid}\n") + val labelStrings = printer(Pair(stateBefore, it)) + if (maxWidth < (labelStrings.maxOfOrNull { it.length } ?: 0)) { + maxWidth = labelStrings.maxOfOrNull { it.length } ?: 0 + } + sequenceFile.appendText("${labelStrings.joinToString("\n")}\n") + sequenceFile.appendText("endhnote\n") + } + trace.actions.map { it.pid }.distinct().reduce { acc, current -> + sequenceFile.appendText("$acc --> $current: \"${" ".repeat(maxWidth)}\"\n") + current + } + sequenceFile.appendText("@enduml\n") } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt index c8446cebd1..f5257effa4 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/XcfaCli.kt @@ -101,7 +101,7 @@ class XcfaCli(private val args: Array) { val logger = ConsoleLogger(config.debugConfig.logLevel) val uniqueLogger = UniqueWarningLogger(logger) - runConfig(config, logger, uniqueLogger) + runConfig(config, logger, uniqueLogger, false) } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt index e0b8bd9406..be171f877c 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToBoundedChecker.kt @@ -20,6 +20,7 @@ import com.google.common.base.Preconditions import hu.bme.mit.theta.analysis.algorithm.SafetyChecker import hu.bme.mit.theta.analysis.algorithm.bounded.BoundedChecker import hu.bme.mit.theta.analysis.algorithm.bounded.MonolithicExpr +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.core.decl.Decls import hu.bme.mit.theta.core.stmt.* @@ -47,7 +48,7 @@ import java.util.stream.Collectors fun getBoundedChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, - logger: Logger): SafetyChecker, XcfaAction, XcfaPrec<*>> { + logger: Logger): SafetyChecker>, XcfaAction, XcfaPrec<*>> { val boundedConfig = config.backendConfig.specConfig as BoundedConfig @@ -66,7 +67,7 @@ fun getBoundedChecker(xcfa: XCFA, mcm: MCM, valToState = { valToState(xcfa, it) }, biValToAction = { val1, val2 -> valToAction(xcfa, val1, val2) }, logger = logger - ) as SafetyChecker, XcfaAction, XcfaPrec<*>> + ) as SafetyChecker>, XcfaAction, XcfaPrec<*>> } diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt index 8add3bc5bc..81084dc51c 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToCegarChecker.kt @@ -27,12 +27,12 @@ import hu.bme.mit.theta.analysis.algorithm.cegar.Refiner import hu.bme.mit.theta.analysis.expr.ExprAction import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.expr.refinement.* +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.analysis.runtimemonitor.CexMonitor import hu.bme.mit.theta.analysis.runtimemonitor.MonitorCheckpoint import hu.bme.mit.theta.analysis.waitlist.PriorityWaitlist import hu.bme.mit.theta.common.logging.Logger -import hu.bme.mit.theta.core.decl.Decl -import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.xcfa.analysis.* @@ -43,27 +43,27 @@ import hu.bme.mit.theta.xcfa.model.XCFA fun getCegarChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, - logger: Logger): SafetyChecker, XcfaAction, XcfaPrec<*>> { + logger: Logger): SafetyChecker>, XcfaAction, XcfaPrec<*>> { val cegarConfig = config.backendConfig.specConfig as CegarConfig val abstractionSolverFactory: SolverFactory = getSolver(cegarConfig.abstractorConfig.abstractionSolver, cegarConfig.abstractorConfig.validateAbstractionSolver) val refinementSolverFactory: SolverFactory = getSolver(cegarConfig.refinerConfig.refinementSolver, cegarConfig.refinerConfig.validateRefinementSolver) - val ignoredVarRegistry = mutableMapOf, MutableSet>() + val ignoredVarRegistry = mutableMapOf, MutableSet>() val lts = cegarConfig.coi.getLts(xcfa, ignoredVarRegistry, cegarConfig.porLevel) val waitlist = if (cegarConfig.porLevel.isDynamic) { (cegarConfig.coi.porLts as XcfaDporLts).waitlist } else { - PriorityWaitlist.create, XcfaAction>>( + PriorityWaitlist.create>, XcfaAction>>( cegarConfig.abstractorConfig.search.getComp(xcfa)) } val abstractionSolverInstance = abstractionSolverFactory.createSolver() - val globalStatePartialOrd: PartialOrd = cegarConfig.abstractorConfig.domain.partialOrd( - abstractionSolverInstance) - val corePartialOrd: PartialOrd> = + val globalStatePartialOrd: PartialOrd> = cegarConfig.abstractorConfig.domain.partialOrd( + abstractionSolverInstance) as PartialOrd> + val corePartialOrd: PartialOrd>> = if (xcfa.isInlined) getPartialOrder(globalStatePartialOrd) else getStackPartialOrder(globalStatePartialOrd) val abstractor: Abstractor = cegarConfig.abstractorConfig.domain.abstractor( @@ -79,7 +79,8 @@ fun getCegarChecker(xcfa: XCFA, mcm: MCM, XcfaDporLts.getPartialOrder(corePartialOrd) } else { corePartialOrd - } + }, + cegarConfig.abstractorConfig.havocMemory ) as Abstractor val ref: ExprTraceChecker = @@ -119,12 +120,12 @@ fun getCegarChecker(xcfa: XCFA, mcm: MCM, MonitorCheckpoint.register(cm, "CegarChecker.unsafeARG") } - return object : SafetyChecker, XcfaAction, XcfaPrec<*>> { - override fun check(prec: XcfaPrec<*>?): SafetyResult, XcfaAction> { - return cegarChecker.check(prec) as SafetyResult, XcfaAction> + return object : SafetyChecker>, XcfaAction, XcfaPrec<*>> { + override fun check(prec: XcfaPrec<*>?): SafetyResult>, XcfaAction> { + return cegarChecker.check(prec) as SafetyResult>, XcfaAction> } - override fun check(): SafetyResult, XcfaAction> { + override fun check(): SafetyResult>, XcfaAction> { return check(cegarConfig.abstractorConfig.domain.initPrec(xcfa, cegarConfig.initPrec)) } } 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 5cece3382a..66451882be 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 @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.cli.checkers 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.frontend.ParseContext import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM @@ -30,16 +31,17 @@ import hu.bme.mit.theta.xcfa.model.XCFA fun getChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, parseContext: ParseContext, logger: Logger, - uniqueLogger: Logger): SafetyChecker, XcfaAction, XcfaPrec<*>> = + uniqueLogger: Logger): SafetyChecker>, XcfaAction, 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, XcfaPrec<*>> { _ -> SafetyResult.unknown() } + Backend.NONE -> SafetyChecker>, XcfaAction, XcfaPrec<*>> { _ -> SafetyResult.unknown() } } } 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 new file mode 100644 index 0000000000..b96b969cad --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/ConfigToOcChecker.kt @@ -0,0 +1,40 @@ +/* + * 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.cli.checkers + +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 +import hu.bme.mit.theta.xcfa.model.XCFA + +fun getOcChecker(xcfa: XCFA, mcm: MCM, + config: XcfaConfig<*, *>, + logger: Logger): SafetyChecker>, XcfaAction, XcfaPrec<*>> { + 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() + } +} \ No newline at end of file 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 3114f4e733..b618e4ba53 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 @@ -19,6 +19,7 @@ package hu.bme.mit.theta.xcfa.cli.checkers import com.google.common.base.Stopwatch 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.frontend.ParseContext import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM @@ -41,7 +42,7 @@ import javax.script.SimpleBindings fun getPortfolioChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, parseContext: ParseContext, logger: Logger, - uniqueLogger: Logger): SafetyChecker, XcfaAction, XcfaPrec<*>> = SafetyChecker { _ -> + uniqueLogger: Logger): SafetyChecker>, XcfaAction, XcfaPrec<*>> = SafetyChecker { _ -> val sw = Stopwatch.createStarted() val portfolioName = (config.backendConfig.specConfig as PortfolioConfig).portfolio @@ -71,5 +72,5 @@ fun getPortfolioChecker(xcfa: XCFA, mcm: MCM, config: XcfaConfig<*, *>, 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>? + result.second as SafetyResult>, XcfaAction>? } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt index 6a1fd70819..30b9236ac3 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/checkers/InProcessChecker.kt @@ -23,6 +23,7 @@ import hu.bme.mit.theta.analysis.Action import hu.bme.mit.theta.analysis.State 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.frontend.ParseContext import hu.bme.mit.theta.xcfa.analysis.XcfaAction @@ -45,13 +46,13 @@ class InProcessChecker( val config: XcfaConfig, val parseContext: ParseContext, val logger: Logger, -) : SafetyChecker, XcfaAction, XcfaPrec<*>> { +) : SafetyChecker>, XcfaAction, XcfaPrec<*>> { - override fun check(prec: XcfaPrec<*>?): SafetyResult, XcfaAction> { + override fun check(prec: XcfaPrec<*>?): SafetyResult>, XcfaAction> { return check() } - override fun check(): SafetyResult, XcfaAction> { + override fun check(): SafetyResult>, XcfaAction> { val tempDir = createTempDirectory(config.outputConfig.resultFolder.toPath()) val xcfaJson = CachingFileSerializer.serialize("xcfa.json", xcfa) { getGson(xcfa).toJson(xcfa) } @@ -122,7 +123,7 @@ class InProcessChecker( } tempDir.toFile().deleteRecursively() - return booleanSafetyResult as SafetyResult, XcfaAction> + return booleanSafetyResult as SafetyResult>, XcfaAction> } private class ProcessHandler : NuAbstractProcessHandler() { diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt index 1cb4db021c..563a7abaa3 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ExitCodes.kt @@ -53,45 +53,48 @@ private fun Throwable.printCauseAndTrace(stacktrace: Boolean) { System.err.println("Process failed! ($location, $this)") } -fun exitOnError(stacktrace: Boolean, debug: Boolean, body: () -> T): T { +fun exitOnError(stacktrace: Boolean, throwDontExit: Boolean, body: () -> T): T { try { return body() + } catch (e: ErrorCodeException) { + e.printStackTrace() + exitProcess(throwDontExit, e, e.code) } catch (e: SmtLibSolverException) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code) + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code) } catch (e: JavaSMTSolverException) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code) + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code) } catch (e: SolverValidationException) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code); } catch (e: Z3Exception) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code); } catch (e: ClassCastException) { e.printCauseAndTrace(stacktrace) if (e.message?.contains("com.microsoft.z3") == true) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code); else - exitProcess(debug, e, ExitCodes.GENERIC_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.GENERIC_ERROR.code); } catch (e: NotSolvableException) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.VERIFICATION_STUCK.code); + exitProcess(throwDontExit, e, ExitCodes.VERIFICATION_STUCK.code); } catch (e: OutOfMemoryError) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.OUT_OF_MEMORY.code); + exitProcess(throwDontExit, e, ExitCodes.OUT_OF_MEMORY.code); } catch (e: UnknownSolverStatusException) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code); } catch (e: RuntimeException) { e.printCauseAndTrace(stacktrace) if (e.message?.contains("Solver problem") == true || e.message?.contains("Z3") == true) { - exitProcess(debug, e, ExitCodes.SOLVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SOLVER_ERROR.code); } else { - exitProcess(debug, e, ExitCodes.SERVER_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.SERVER_ERROR.code); } } catch (e: Exception) { e.printCauseAndTrace(stacktrace) - exitProcess(debug, e, ExitCodes.GENERIC_ERROR.code); + exitProcess(throwDontExit, e, ExitCodes.GENERIC_ERROR.code); } } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt index 5f428a36b0..14f684f6d7 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/params/ParamValues.kt @@ -34,10 +34,15 @@ import hu.bme.mit.theta.analysis.expr.ExprState import hu.bme.mit.theta.analysis.expr.refinement.* import hu.bme.mit.theta.analysis.pred.* import hu.bme.mit.theta.analysis.pred.ExprSplitters.ExprSplitter +import hu.bme.mit.theta.analysis.ptr.ItpRefToPtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrPrec +import hu.bme.mit.theta.analysis.ptr.PtrState +import hu.bme.mit.theta.analysis.ptr.getPtrPartialOrd import hu.bme.mit.theta.analysis.waitlist.Waitlist import hu.bme.mit.theta.common.logging.Logger -import hu.bme.mit.theta.core.decl.Decl +import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.type.booltype.BoolExprs +import hu.bme.mit.theta.core.utils.ExprUtils import hu.bme.mit.theta.solver.Solver import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.xcfa.analysis.* @@ -61,13 +66,14 @@ enum class InputType { enum class Backend { CEGAR, BOUNDED, + OC, LAZY, PORTFOLIO, NONE, } enum class POR( - val getLts: (XCFA, MutableMap, MutableSet>) -> LTS, XcfaAction>, + val getLts: (XCFA, MutableMap, MutableSet>) -> LTS>, XcfaAction>, val isDynamic: Boolean, val isAbstractionAware: Boolean ) { @@ -92,77 +98,78 @@ enum class Domain( xcfa: XCFA, solver: Solver, maxEnum: Int, - waitlist: Waitlist, XcfaAction>>, - stopCriterion: StopCriterion, XcfaAction>, + waitlist: Waitlist>, XcfaAction>>, + stopCriterion: StopCriterion>, XcfaAction>, logger: Logger, - lts: LTS, XcfaAction>, + lts: LTS>, XcfaAction>, errorDetectionType: ErrorDetection, - partialOrd: PartialOrd> + partialOrd: PartialOrd>>, + isHavoc: Boolean ) -> Abstractor, val itpPrecRefiner: (exprSplitter: ExprSplitter) -> PrecRefiner, - val initPrec: (XCFA, InitPrec) -> XcfaPrec<*>, - val partialOrd: (Solver) -> PartialOrd, + val initPrec: (XCFA, InitPrec) -> XcfaPrec>, + val partialOrd: (Solver) -> PartialOrd>, val nodePruner: NodePruner, val stateType: Type ) { EXPL( - abstractor = { a, b, c, d, e, f, g, h, i -> - getXcfaAbstractor(ExplXcfaAnalysis(a, b, c, i as PartialOrd>), d, + abstractor = { a, b, c, d, e, f, g, h, i, j -> + getXcfaAbstractor(ExplXcfaAnalysis(a, b, c, i as PartialOrd>>, j), d, e, f, g, h) }, itpPrecRefiner = { - XcfaPrecRefiner(ItpRefToExplPrec()) + XcfaPrecRefiner, ExplPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToExplPrec())) }, initPrec = { x, ip -> ip.explPrec(x) }, - partialOrd = { PartialOrd { s1, s2 -> s1.isLeq(s2) } }, - nodePruner = AtomicNodePruner, XcfaAction>(), + partialOrd = { PartialOrd { s1, s2 -> s1.isLeq(s2) }.getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), stateType = TypeToken.get(ExplState::class.java).type ), PRED_BOOL( - abstractor = { a, b, c, d, e, f, g, h, i -> + abstractor = { a, b, c, d, e, f, g, h, i, j -> getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.booleanAbstractor(b), - i as PartialOrd>), d, e, f, g, h) + i as PartialOrd>>, j), d, e, f, g, h) }, itpPrecRefiner = { a -> - XcfaPrecRefiner(ItpRefToPredPrec(a)) + XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) }, initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver) }, - nodePruner = AtomicNodePruner, XcfaAction>(), + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), stateType = TypeToken.get(PredState::class.java).type ), PRED_CART( - abstractor = { a, b, c, d, e, f, g, h, i -> + abstractor = { a, b, c, d, e, f, g, h, i, j -> getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.cartesianAbstractor(b), - i as PartialOrd>), d, e, f, g, h) + i as PartialOrd>>, j), d, e, f, g, h) }, itpPrecRefiner = { a -> - XcfaPrecRefiner(ItpRefToPredPrec(a)) + XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) }, initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver) }, - nodePruner = AtomicNodePruner, XcfaAction>(), + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), stateType = TypeToken.get(PredState::class.java).type ), PRED_SPLIT( - abstractor = { a, b, c, d, e, f, g, h, i -> + abstractor = { a, b, c, d, e, f, g, h, i, j -> getXcfaAbstractor(PredXcfaAnalysis(a, b, PredAbstractors.booleanSplitAbstractor(b), - i as PartialOrd>), d, e, f, g, h) + i as PartialOrd>>, j), d, e, f, g, h) }, itpPrecRefiner = { a -> - XcfaPrecRefiner(ItpRefToPredPrec(a)) + XcfaPrecRefiner, PredPrec, ItpRefutation>(ItpRefToPtrPrec(ItpRefToPredPrec(a))) }, initPrec = { x, ip -> ip.predPrec(x) }, - partialOrd = { solver -> PredOrd.create(solver) }, - nodePruner = AtomicNodePruner, XcfaAction>(), + partialOrd = { solver -> PredOrd.create(solver).getPtrPartialOrd() }, + nodePruner = AtomicNodePruner>, XcfaAction>(), stateType = TypeToken.get(PredState::class.java).type ), } enum class Refinement( val refiner: (solverFactory: SolverFactory, monitorOption: CexMonitorOptions) -> ExprTraceChecker, - val stopCriterion: StopCriterion, XcfaAction>, + val stopCriterion: StopCriterion>, XcfaAction>, ) { FW_BIN_ITP( @@ -294,31 +301,34 @@ enum class Search { } enum class InitPrec( - val explPrec: (xcfa: XCFA) -> XcfaPrec, - val predPrec: (xcfa: XCFA) -> XcfaPrec, + val explPrec: (xcfa: XCFA) -> XcfaPrec>, + val predPrec: (xcfa: XCFA) -> XcfaPrec>, ) { EMPTY( - explPrec = { XcfaPrec(ExplPrec.empty()) }, - predPrec = { XcfaPrec(PredPrec.of()) } + explPrec = { XcfaPrec(PtrPrec(ExplPrec.empty(), emptySet())) }, + predPrec = { XcfaPrec(PtrPrec(PredPrec.of(), emptySet())) } ), ALLVARS( - explPrec = { xcfa -> XcfaPrec(ExplPrec.of(xcfa.collectVars())) }, + explPrec = { xcfa -> XcfaPrec(PtrPrec(ExplPrec.of(xcfa.collectVars()), emptySet())) }, predPrec = { error("ALLVARS is not interpreted for the predicate domain.") } ), ALLGLOBALS( - explPrec = { xcfa -> XcfaPrec(ExplPrec.of(xcfa.vars.map { it.wrappedVar })) }, + explPrec = { xcfa -> XcfaPrec(PtrPrec(ExplPrec.of(xcfa.vars.map { it.wrappedVar }), emptySet())) }, predPrec = { error("ALLGLOBALS is not interpreted for the predicate domain.") } ), ALLASSUMES( - explPrec = { error("ALLVARS is not interpreted for the predicate domain.") }, - predPrec = { xcfa -> XcfaPrec(PredPrec.of(xcfa.collectAssumes())) }, + explPrec = { xcfa -> + XcfaPrec( + PtrPrec(ExplPrec.of(xcfa.collectAssumes().flatMap(ExprUtils::getVars)), emptySet())) + }, + predPrec = { xcfa -> XcfaPrec(PtrPrec(PredPrec.of(xcfa.collectAssumes()), emptySet())) }, ), } enum class ConeOfInfluenceMode( - val getLts: (XCFA, MutableMap, MutableSet>, POR) -> LTS, XcfaAction> + val getLts: (XCFA, MutableMap, MutableSet>, POR) -> LTS>, XcfaAction> ) { NO_COI({ xcfa, ivr, por -> @@ -328,17 +338,19 @@ enum class ConeOfInfluenceMode( ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { COI.porLts = it } ConeOfInfluence.lts }), - POR_COI({ xcfa, ivr, _ -> + POR_COI({ xcfa, ivr, por -> ConeOfInfluence.coreLts = getXcfaLts() - XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) }), POR_COI_POR({ xcfa, ivr, por -> ConeOfInfluence.coreLts = por.getLts(xcfa, ivr).also { POR_COI_POR.porLts = it } - XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + if (por.isAbstractionAware) XcfaAasporCoiLts(xcfa, ivr, ConeOfInfluence.lts) + else XcfaSporCoiLts(xcfa, ConeOfInfluence.lts) }) ; - var porLts: LTS, XcfaAction>? = null + var porLts: LTS>, XcfaAction>? = null } // TODO CexMonitor: disable for multi_seq 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 6e92dd9e6f..ff6d663986 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 @@ -25,6 +25,7 @@ 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.OcDecisionProcedureType import hu.bme.mit.theta.xcfa.model.XCFA import hu.bme.mit.theta.xcfa.passes.LbePass import java.io.File @@ -96,8 +97,20 @@ 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 = ["--unroll"], description = "Max number of loop iterations to unroll") - var loopUnroll: Int = 50, + @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, @@ -120,6 +133,9 @@ data class FrontendConfig( 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, ) : SpecFrontendConfig data class CHCFrontendConfig( @@ -149,6 +165,7 @@ data class BackendConfig( specConfig = when (backend) { Backend.CEGAR -> CegarConfig() as T Backend.BOUNDED -> BoundedConfig() as T + Backend.OC -> OcConfig() as T Backend.LAZY -> null Backend.PORTFOLIO -> PortfolioConfig() as T Backend.NONE -> null @@ -169,8 +186,8 @@ data class CegarConfig( @Parameter(names = ["--coi"], description = "Enable ConeOfInfluence") var coi: ConeOfInfluenceMode = ConeOfInfluenceMode.NO_COI, - @Parameter(names = ["--cex-monitor"], description = "Option to enable CexMonitor") - var cexMonitor: CexMonitorOptions = CexMonitorOptions.DISABLE, + @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() @@ -201,6 +218,10 @@ data class CegarAbstractorConfig( @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( @@ -287,6 +308,11 @@ data class InterpolationConfig( ) : Config +data class OcConfig( + @Parameter(names = ["--oc-decision-procedure"], description = "Decision procedure for ordering-consistency check") + var decisionProcedure: OcDecisionProcedureType = OcDecisionProcedureType.PROPAGATOR, +) : SpecBackendConfig + data class PortfolioConfig( @Parameter(names = ["--portfolio"], description = "Portfolio to run") var portfolio: String = "COMPLEX", @@ -296,6 +322,9 @@ 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(), 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 d4f59015df..c6901d3838 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 @@ -35,7 +35,7 @@ fun complexPortfolio23(xcfa: XCFA, mcm: MCM, logger: Logger, uniqueLogger: Logger): STM { - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger) } + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } var baseConfig = XcfaConfig( inputConfig = InputConfig( 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 b7569688f9..ca6f147553 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 @@ -39,7 +39,7 @@ fun complexPortfolio24( logger: Logger, uniqueLogger: Logger): STM { - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger) } + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } var baseConfig = XcfaConfig( inputConfig = InputConfig( @@ -81,7 +81,8 @@ fun complexPortfolio24( resultFolder = Paths.get("./").toFile(), // cwd cOutputConfig = COutputConfig(disable = true), witnessConfig = WitnessConfig(disable = false, concretizerSolver = "Z3", validateConcretizerSolver = false), - argConfig = ArgConfig(disable = true) + argConfig = ArgConfig(disable = true), + enableOutput = portfolioConfig.outputConfig.enableOutput, ), debugConfig = portfolioConfig.debugConfig ) @@ -106,12 +107,12 @@ fun complexPortfolio24( baseConfig = baseConfig.copy(backendConfig = baseConfig.backendConfig.copy(specConfig = recursiveConfig)) } - val timeoutTrigger = ExceptionTrigger( + val timeoutOrNotSolvableError = ExceptionTrigger( fallthroughExceptions = setOf( ErrorCodeException(ExitCodes.SOLVER_ERROR.code), ErrorCodeException(ExitCodes.SERVER_ERROR.code), ), - label = "TimeoutOrGenericError" + label = "TimeoutOrNotSolvableError" ) val timeoutOrSolverError = ExceptionTrigger( @@ -199,9 +200,9 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_cvc5, config_BITWISE_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + if (inProcess) timeoutOrNotSolvableError else anyError)) edges.add(Edge(config_BITWISE_EXPL_NWT_IT_WP_Z3, config_BITWISE_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -224,7 +225,7 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_BITWISE_PRED_CART_SEQ_ITP_mathsat, config_BITWISE_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -275,9 +276,9 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_cvc5, config_FLOAT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + if (inProcess) timeoutOrNotSolvableError else anyError)) edges.add(Edge(config_FLOAT_EXPL_NWT_IT_WP_Z3, config_FLOAT_PRED_CART_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -300,7 +301,7 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_FLOAT_PRED_CART_SEQ_ITP_mathsat, config_FLOAT_EXPL_SEQ_ITP_mathsat, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -341,7 +342,7 @@ fun complexPortfolio24( timeoutMs = 300000 ), checker) edges.add(Edge(config_LIN_INT_EXPL_NWT_IT_WP_mathsat, config_LIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -364,7 +365,7 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_LIN_INT_EXPL_SEQ_ITP_Z3, config_LIN_INT_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -416,9 +417,19 @@ fun complexPortfolio24( timeoutMs = 100000 ), checker) edges.add(Edge(config_NONLIN_INT_EXPL_NWT_IT_WP_Z3, config_NONLIN_INT_EXPL_SEQ_ITP_Z3, - if (inProcess) timeoutTrigger else anyError)) + 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, @@ -429,6 +440,8 @@ fun complexPortfolio24( 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( @@ -441,6 +454,17 @@ fun complexPortfolio24( ), 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, @@ -451,6 +475,8 @@ fun complexPortfolio24( 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( @@ -479,7 +505,7 @@ fun complexPortfolio24( timeoutMs = 300000 ), checker) edges.add(Edge(config_ARR_EXPL_NWT_IT_WP_cvc5, config_ARR_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -502,7 +528,7 @@ fun complexPortfolio24( timeoutMs = 500000 ), checker) edges.add(Edge(config_ARR_PRED_CART_SEQ_ITP_Z3, config_ARR_PRED_CART_SEQ_ITP_princess, - if (inProcess) timeoutTrigger else anyError)) + 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", @@ -544,7 +570,7 @@ fun complexPortfolio24( timeoutMs = 300000 ), checker) edges.add(Edge(config_MULTITHREAD_EXPL_SEQ_ITP_Z3, config_MULTITHREAD_EXPL_NWT_IT_WP_z3, - if (inProcess) timeoutTrigger else anyError)) + 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( @@ -567,7 +593,7 @@ fun complexPortfolio24( timeoutMs = 0 ), checker) edges.add(Edge(config_MULTITHREAD_EXPL_NWT_IT_WP_z3, config_MULTITHREAD_PRED_CART_SEQ_ITP_Z3, - if (inProcess) timeoutTrigger else anyError)) + 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( 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 af771dc8a6..d98b44e8f9 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 @@ -50,7 +50,7 @@ class ConfigNode(name: String, private val config: XcfaConfig<*, *>, data class Edge(val source: Node, val target: Node, - val trigger: (Exception) -> Boolean, + val trigger: (Throwable) -> Boolean, val guard: (Node, Edge) -> Boolean = { _, _ -> true }) { init { @@ -65,15 +65,15 @@ data class Edge(val source: Node, // if the exceptions set is empty, it catches all exceptions class ExceptionTrigger( - val exceptions: Set = emptySet(), - val fallthroughExceptions: Set = emptySet(), + val exceptions: Set = emptySet(), + val fallthroughExceptions: Set = emptySet(), val label: String? = null -) : (Exception) -> Boolean { +) : (Throwable) -> Boolean { - constructor(vararg exceptions: Exception, label: String? = null) : this(exceptions.toSet(), + constructor(vararg exceptions: Throwable, label: String? = null) : this(exceptions.toSet(), label = label) - override fun invoke(e: Exception): Boolean = + override fun invoke(e: Throwable): Boolean = if (exceptions.isNotEmpty()) exceptions.contains(e) && !fallthroughExceptions.contains(e) else @@ -109,7 +109,7 @@ ${edges.map { it.visualize() }.reduce { a, b -> "$a\n$b" }} while (true) { try { return currentNode.execute() - } catch (e: Exception) { + } catch (e: Throwable) { println("Caught exception: $e") val edge: Edge? = currentNode.outEdges.find { it.trigger(e) } if (edge != null) { diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/BMCValToTrace.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/BMCValToTrace.kt index 79bc802e1c..7a04fcb462 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/BMCValToTrace.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/BMCValToTrace.kt @@ -17,6 +17,7 @@ package hu.bme.mit.theta.xcfa.cli.utils import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.core.decl.Decls.Var import hu.bme.mit.theta.core.model.ImmutableValuation import hu.bme.mit.theta.core.model.Valuation @@ -44,7 +45,7 @@ fun valToAction(xcfa: XCFA, val1: Valuation, val2: Valuation): XcfaAction { }) } -fun valToState(xcfa: XCFA, val1: Valuation): XcfaState { +fun valToState(xcfa: XCFA, val1: Valuation): XcfaState> { val valMap = val1.toMap() var i = 0 val map: MutableMap = HashMap() @@ -58,11 +59,11 @@ fun valToState(xcfa: XCFA, val1: Valuation): XcfaState { listOf(map[(valMap[valMap.keys.first { it.name == "__loc_" }] as IntLitExpr).value.toInt()])), varLookup = LinkedList(), ))), - ExplState.of( + PtrState(ExplState.of( ImmutableValuation.from( val1.toMap() .filter { it.key.name != "__loc_" && !it.key.name.startsWith("__temp_") } - .map { Pair(Var("_" + "_" + it.key.name, it.key.type), it.value) }.toMap())), + .map { Pair(Var("_" + "_" + it.key.name, it.key.type), it.value) }.toMap()))), mutexes = emptyMap(), threadLookup = emptyMap(), bottom = false 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 f17ea760f7..cb18d5c237 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 @@ -89,7 +89,7 @@ private fun parseC(input: File, explicitProperty: ErrorDetection, parseContext: val stream = FileInputStream(input) val xcfa = getXcfaFromC(stream, parseContext, false, explicitProperty == ErrorDetection.OVERFLOW, uniqueWarningLogger).first - parseContext.arithmeticTraits.add(ArithmeticTrait.BITWISE) + parseContext.addArithmeticTrait(ArithmeticTrait.BITWISE) xcfa } else { throw e diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaWitnessWriter.kt b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaWitnessWriter.kt index c7c615f222..e7fee06943 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaWitnessWriter.kt +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/utils/XcfaWitnessWriter.kt @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.cli.utils import hu.bme.mit.theta.analysis.Trace import hu.bme.mit.theta.analysis.algorithm.SafetyResult import hu.bme.mit.theta.analysis.expl.ExplState +import hu.bme.mit.theta.analysis.ptr.PtrState import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.solver.SolverFactory import hu.bme.mit.theta.xcfa.analysis.XcfaAction @@ -44,7 +45,8 @@ class XcfaWitnessWriter { ) { if (safetyResult.isUnsafe && safetyResult.asUnsafe().hasTrace()) { val concrTrace: Trace, XcfaAction> = XcfaTraceConcretizer.concretize( - safetyResult.asUnsafe().trace as Trace, XcfaAction>?, cexSolverFactory) + safetyResult.asUnsafe().trace as Trace>, XcfaAction>?, cexSolverFactory, + parseContext) val witnessTrace = traceToWitness(trace = concrTrace, parseContext = parseContext) val witness = Witness(witnessTrace, inputFile) diff --git a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/witnesses/XcfaTraceConcretizer.java b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/witnesses/XcfaTraceConcretizer.java index b0647673a3..5c6c8e0a96 100644 --- a/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/witnesses/XcfaTraceConcretizer.java +++ b/subprojects/xcfa/xcfa-cli/src/main/java/hu/bme/mit/theta/xcfa/cli/witnesses/XcfaTraceConcretizer.java @@ -23,15 +23,31 @@ import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceFwBinItpChecker; import hu.bme.mit.theta.analysis.expr.refinement.ExprTraceStatus; import hu.bme.mit.theta.analysis.expr.refinement.ItpRefutation; +import hu.bme.mit.theta.analysis.ptr.PtrState; +import hu.bme.mit.theta.core.decl.Decl; +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.Type; import hu.bme.mit.theta.core.type.booltype.BoolExprs; +import hu.bme.mit.theta.core.type.inttype.IntType; +import hu.bme.mit.theta.core.utils.ExprUtils; +import hu.bme.mit.theta.frontend.ParseContext; import hu.bme.mit.theta.solver.SolverFactory; import hu.bme.mit.theta.xcfa.analysis.XcfaAction; import hu.bme.mit.theta.xcfa.analysis.XcfaState; import hu.bme.mit.theta.xcfa.model.XcfaEdge; +import kotlin.Triple; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -41,15 +57,19 @@ */ public class XcfaTraceConcretizer { public static Trace, XcfaAction> concretize( - final Trace, XcfaAction> trace, SolverFactory solverFactory) { - List> sbeStates = new ArrayList<>(); + final Trace>, XcfaAction> trace, SolverFactory solverFactory, ParseContext parseContext) { + List>> sbeStates = new ArrayList<>(); List sbeActions = new ArrayList<>(); - sbeStates.add(trace.getState(0)); + sbeStates.add(trace.getState(0).withState(new PtrState<>(ExplState.top()))); + + Map, Expr, Expr>>> nextW = Collections.emptyMap(); for (int i = 0; i < trace.getActions().size(); ++i) { final XcfaEdge edge = new XcfaEdge(trace.getAction(i).getSource(), trace.getAction(i).getTarget(), trace.getAction(i).getLabel()); - sbeActions.add(new XcfaAction(trace.getAction(i).getPid(), edge)); - sbeStates.add(trace.getState(i + 1)); + final XcfaAction action = new XcfaAction(trace.getAction(i).getPid(), edge, nextW, trace.getAction(i).getInCnt()); + sbeActions.add(action); + nextW = action.nextWriteTriples(); + sbeStates.add(trace.getState(i + 1).withState(new PtrState<>(ExplState.top()))); } Trace, XcfaAction> sbeTrace = Trace.of(sbeStates, sbeActions); final ExprTraceChecker checker = ExprTraceFwBinItpChecker.create(BoolExprs.True(), @@ -61,11 +81,15 @@ public static Trace, XcfaAction> concretize( assert valuations.getStates().size() == sbeTrace.getStates().size(); final List> cfaStates = new ArrayList<>(); + final Set> varSoFar = new LinkedHashSet<>(); for (int i = 0; i < sbeTrace.getStates().size(); ++i) { - cfaStates.add(new XcfaState<>(null, sbeTrace.getState(i).getProcesses(), ExplState.of(valuations.getState(i)))); + cfaStates.add(new XcfaState<>(null, sbeTrace.getState(i).getProcesses(), ExplState.of(ImmutableValuation.from(valuations.getState(i).toMap().entrySet().stream().filter(it -> varSoFar.contains(it.getKey())).collect(Collectors.toMap(Map.Entry, LitExpr>::getKey, Map.Entry::getValue)))))); + if (i < sbeTrace.getActions().size()) { + varSoFar.addAll(ExprUtils.getVars(sbeTrace.getAction(i).toExpr())); + } } - return Trace.of(cfaStates, sbeTrace.getActions()); + return Trace.of(cfaStates, sbeActions); } -} \ No newline at end of file +} diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt index 7081c48c0f..c1e5f7855d 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliParseTest.kt @@ -54,6 +54,7 @@ class XcfaCliParseTest { Arguments.of("/c/litmustest/singlethread/21namecollision.c"), Arguments.of("/c/litmustest/singlethread/22nondet.c"), Arguments.of("/c/litmustest/singlethread/23overflow.c"), + Arguments.of("/c/litmustest/singlethread/25malloc.c"), ) } @@ -229,6 +230,7 @@ class XcfaCliParseTest { fun testJSONParseRoundTrip(filePath: String) { val temp = createTempDirectory() main(arrayOf( + "--enable-output", "--input-type", "C", "--input", javaClass.getResource(filePath)!!.path, "--backend", "NONE", @@ -252,6 +254,7 @@ class XcfaCliParseTest { fun testCParseRoundTrip(filePath: String) { val temp = createTempDirectory() main(arrayOf( + "--enable-output", "--input-type", "C", "--input", javaClass.getResource(filePath)!!.path, "--backend", "NONE", 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 cb553ca391..18fcb28e58 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 @@ -65,7 +65,6 @@ class XcfaCliVerifyTest { 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/13typedef.c", "--domain PRED_CART"), Arguments.of("/c/litmustest/singlethread/14ushort.c", null), Arguments.of("/c/litmustest/singlethread/15addition.c", null), @@ -174,6 +173,7 @@ class XcfaCliVerifyTest { fun testCWitness(filePath: String, extraArgs: String?) { val temp = createTempDirectory() val params = arrayOf( + "--enable-output", "--input-type", "C", "--input", javaClass.getResource(filePath)!!.path, "--stacktrace", diff --git a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt index 187af6325b..bc5657f5ba 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt +++ b/subprojects/xcfa/xcfa-cli/src/test/java/hu/bme/mit/theta/xcfa/cli/XcfaCliWitnessTest.kt @@ -43,8 +43,8 @@ class XcfaCliWitnessTest { WitnessEdge( startlineRange = Pair(5, 5), endlineRange = Pair(5, 5), - startoffsetRange = Pair(79, 95), - endoffsetRange = Pair(110, 125), + startoffsetRange = Pair(100, 130), + endoffsetRange = Pair(100, 130), assumption = Regex("i *== *-1"), ), )), @@ -52,8 +52,8 @@ class XcfaCliWitnessTest { WitnessEdge( startlineRange = Pair(5, 5), endlineRange = Pair(5, 5), - startoffsetRange = Pair(79, 95), - endoffsetRange = Pair(110, 125), + startoffsetRange = Pair(100, 130), + endoffsetRange = Pair(100, 130), assumption = Regex("i *== *-1"), ), )), @@ -67,6 +67,7 @@ class XcfaCliWitnessTest { fun testCWitness(filePath: String, extraArgs: String?, expectedWitnessEdges: List) { val temp = createTempDirectory() val params = arrayOf( + "--enable-output", "--input-type", "C", "--input", javaClass.getResource(filePath)!!.path, *(extraArgs?.split(" ")?.toTypedArray() ?: emptyArray()), diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/12ptrtypes.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/12ptrtypes.c index f42a220292..aa785ff812 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/12ptrtypes.c +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/12ptrtypes.c @@ -1,6 +1,6 @@ void reach_error(){} -void check_geq_110(void* param) { +void check_geq_110(unsigned int* param) { if(*(unsigned int*)param <= 110) reach_error(); } diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/18multithread.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/18multithread.c index 8ca102134e..1429d06ae4 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/18multithread.c +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/18multithread.c @@ -686,6 +686,9 @@ extern int pthread_atfork (void (*__prepare) (void), void reach_error(){} +extern void __VERIFIER_atomic_begin(); +extern void __VERIFIER_atomic_end(); + int x = 0; int ERR = 0; diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/21namecollision.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/21namecollision.c index f4df8686d0..6011759feb 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/21namecollision.c +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/21namecollision.c @@ -1,3 +1,4 @@ +void reach_error(){} int f(int x) { return x - 1; } diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/22nondet.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/22nondet.c index 66461be532..b17fdd1a9d 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/22nondet.c +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/22nondet.c @@ -1,3 +1,4 @@ +void reach_error(){} extern int __VERIFIER_nondet_int(); int main() { int i = 0; diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/25malloc.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/25malloc.c new file mode 100644 index 0000000000..25dfdc46b2 --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/25malloc.c @@ -0,0 +1,19 @@ +void reach_error(){} + +int* malloc(int); + +int main() { + int k=10; + int* arr = malloc(42); + + for(int i = 0; i < k; i++) { + arr[i] = i * 2; + } + + for(int i = 0; i < k; i++) { + if(arr[i] % 2) { + reach_error(); + } + } + +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/26thread-ptr.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/26thread-ptr.c new file mode 100644 index 0000000000..532f8ac897 --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/26thread-ptr.c @@ -0,0 +1,959 @@ +// This file is part of the SV-Benchmarks collection of verification tasks: +// https://github.com/sosy-lab/sv-benchmarks +// +// SPDX-FileCopyrightText: 2011-2020 The SV-Benchmarks community +// SPDX-FileCopyrightText: 2020 The ESBMC project +// +// SPDX-License-Identifier: Apache-2.0 + +extern void abort(void); + +extern void __assert_fail (const char *__assertion, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert_perror_fail (int __errnum, const char *__file, + unsigned int __line, const char *__function) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); +extern void __assert (const char *__assertion, const char *__file, int __line) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__noreturn__)); + +void reach_error() { ((void) sizeof ((0) ? 1 : 0), __extension__ ({ if (0) ; else __assert_fail ("0", "stack-1.c", 3, __extension__ __PRETTY_FUNCTION__); })); } +extern void abort(void); +void assume_abort_if_not(int cond) { + if(!cond) {abort();} +} +typedef unsigned char __u_char; +typedef unsigned short int __u_short; +typedef unsigned int __u_int; +typedef unsigned long int __u_long; +typedef signed char __int8_t; +typedef unsigned char __uint8_t; +typedef signed short int __int16_t; +typedef unsigned short int __uint16_t; +typedef signed int __int32_t; +typedef unsigned int __uint32_t; +__extension__ typedef signed long long int __int64_t; +__extension__ typedef unsigned long long int __uint64_t; +__extension__ typedef long long int __quad_t; +__extension__ typedef unsigned long long int __u_quad_t; +__extension__ typedef long long int __intmax_t; +__extension__ typedef unsigned long long int __uintmax_t; +__extension__ typedef __u_quad_t __dev_t; +__extension__ typedef unsigned int __uid_t; +__extension__ typedef unsigned int __gid_t; +__extension__ typedef unsigned long int __ino_t; +__extension__ typedef __u_quad_t __ino64_t; +__extension__ typedef unsigned int __mode_t; +__extension__ typedef unsigned int __nlink_t; +__extension__ typedef long int __off_t; +__extension__ typedef __quad_t __off64_t; +__extension__ typedef int __pid_t; +__extension__ typedef struct { int __val[2]; } __fsid_t; +__extension__ typedef long int __clock_t; +__extension__ typedef unsigned long int __rlim_t; +__extension__ typedef __u_quad_t __rlim64_t; +__extension__ typedef unsigned int __id_t; +__extension__ typedef long int __time_t; +__extension__ typedef unsigned int __useconds_t; +__extension__ typedef long int __suseconds_t; +__extension__ typedef int __daddr_t; +__extension__ typedef int __key_t; +__extension__ typedef int __clockid_t; +__extension__ typedef void * __timer_t; +__extension__ typedef long int __blksize_t; +__extension__ typedef long int __blkcnt_t; +__extension__ typedef __quad_t __blkcnt64_t; +__extension__ typedef unsigned long int __fsblkcnt_t; +__extension__ typedef __u_quad_t __fsblkcnt64_t; +__extension__ typedef unsigned long int __fsfilcnt_t; +__extension__ typedef __u_quad_t __fsfilcnt64_t; +__extension__ typedef int __fsword_t; +__extension__ typedef int __ssize_t; +__extension__ typedef long int __syscall_slong_t; +__extension__ typedef unsigned long int __syscall_ulong_t; +typedef __off64_t __loff_t; +typedef char *__caddr_t; +__extension__ typedef int __intptr_t; +__extension__ typedef unsigned int __socklen_t; +typedef int __sig_atomic_t; +static __inline unsigned int +__bswap_32 (unsigned int __bsx) +{ + return __builtin_bswap32 (__bsx); +} +static __inline __uint64_t +__bswap_64 (__uint64_t __bsx) +{ + return __builtin_bswap64 (__bsx); +} +static __inline __uint16_t +__uint16_identity (__uint16_t __x) +{ + return __x; +} +static __inline __uint32_t +__uint32_identity (__uint32_t __x) +{ + return __x; +} +static __inline __uint64_t +__uint64_identity (__uint64_t __x) +{ + return __x; +} +typedef unsigned int size_t; +typedef __time_t time_t; +struct timespec +{ + __time_t tv_sec; + __syscall_slong_t tv_nsec; +}; +typedef __pid_t pid_t; +struct sched_param +{ + int sched_priority; +}; + + +typedef unsigned long int __cpu_mask; +typedef struct +{ + __cpu_mask __bits[1024 / (8 * sizeof (__cpu_mask))]; +} cpu_set_t; + +extern int __sched_cpucount (size_t __setsize, const cpu_set_t *__setp) + __attribute__ ((__nothrow__ , __leaf__)); +extern cpu_set_t *__sched_cpualloc (size_t __count) __attribute__ ((__nothrow__ , __leaf__)) ; +extern void __sched_cpufree (cpu_set_t *__set) __attribute__ ((__nothrow__ , __leaf__)); + + +extern int sched_setparam (__pid_t __pid, const struct sched_param *__param) + __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_getparam (__pid_t __pid, struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_setscheduler (__pid_t __pid, int __policy, + const struct sched_param *__param) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_getscheduler (__pid_t __pid) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_yield (void) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_get_priority_max (int __algorithm) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_get_priority_min (int __algorithm) __attribute__ ((__nothrow__ , __leaf__)); +extern int sched_rr_get_interval (__pid_t __pid, struct timespec *__t) __attribute__ ((__nothrow__ , __leaf__)); + +typedef __clock_t clock_t; +struct tm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + long int tm_gmtoff; + const char *tm_zone; +}; +typedef __clockid_t clockid_t; +typedef __timer_t timer_t; +struct itimerspec + { + struct timespec it_interval; + struct timespec it_value; + }; +struct sigevent; +struct __locale_struct +{ + struct __locale_data *__locales[13]; + const unsigned short int *__ctype_b; + const int *__ctype_tolower; + const int *__ctype_toupper; + const char *__names[13]; +}; +typedef struct __locale_struct *__locale_t; +typedef __locale_t locale_t; + +extern clock_t clock (void) __attribute__ ((__nothrow__ , __leaf__)); +extern time_t time (time_t *__timer) __attribute__ ((__nothrow__ , __leaf__)); +extern double difftime (time_t __time1, time_t __time0) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)); +extern time_t mktime (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__)); +extern size_t strftime (char *__restrict __s, size_t __maxsize, + const char *__restrict __format, + const struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__)); +extern size_t strftime_l (char *__restrict __s, size_t __maxsize, + const char *__restrict __format, + const struct tm *__restrict __tp, + locale_t __loc) __attribute__ ((__nothrow__ , __leaf__)); +extern struct tm *gmtime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__)); +extern struct tm *localtime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__)); +extern struct tm *gmtime_r (const time_t *__restrict __timer, + struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__)); +extern struct tm *localtime_r (const time_t *__restrict __timer, + struct tm *__restrict __tp) __attribute__ ((__nothrow__ , __leaf__)); +extern char *asctime (const struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__)); +extern char *ctime (const time_t *__timer) __attribute__ ((__nothrow__ , __leaf__)); +extern char *asctime_r (const struct tm *__restrict __tp, + char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)); +extern char *ctime_r (const time_t *__restrict __timer, + char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)); +extern char *__tzname[2]; +extern int __daylight; +extern long int __timezone; +extern char *tzname[2]; +extern void tzset (void) __attribute__ ((__nothrow__ , __leaf__)); +extern int daylight; +extern long int timezone; +extern int stime (const time_t *__when) __attribute__ ((__nothrow__ , __leaf__)); +extern time_t timegm (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__)); +extern time_t timelocal (struct tm *__tp) __attribute__ ((__nothrow__ , __leaf__)); +extern int dysize (int __year) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)); +extern int nanosleep (const struct timespec *__requested_time, + struct timespec *__remaining); +extern int clock_getres (clockid_t __clock_id, struct timespec *__res) __attribute__ ((__nothrow__ , __leaf__)); +extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __attribute__ ((__nothrow__ , __leaf__)); +extern int clock_settime (clockid_t __clock_id, const struct timespec *__tp) + __attribute__ ((__nothrow__ , __leaf__)); +extern int clock_nanosleep (clockid_t __clock_id, int __flags, + const struct timespec *__req, + struct timespec *__rem); +extern int clock_getcpuclockid (pid_t __pid, clockid_t *__clock_id) __attribute__ ((__nothrow__ , __leaf__)); +extern int timer_create (clockid_t __clock_id, + struct sigevent *__restrict __evp, + timer_t *__restrict __timerid) __attribute__ ((__nothrow__ , __leaf__)); +extern int timer_delete (timer_t __timerid) __attribute__ ((__nothrow__ , __leaf__)); +extern int timer_settime (timer_t __timerid, int __flags, + const struct itimerspec *__restrict __value, + struct itimerspec *__restrict __ovalue) __attribute__ ((__nothrow__ , __leaf__)); +extern int timer_gettime (timer_t __timerid, struct itimerspec *__value) + __attribute__ ((__nothrow__ , __leaf__)); +extern int timer_getoverrun (timer_t __timerid) __attribute__ ((__nothrow__ , __leaf__)); +extern int timespec_get (struct timespec *__ts, int __base) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); + +struct __pthread_rwlock_arch_t +{ + unsigned int __readers; + unsigned int __writers; + unsigned int __wrphase_futex; + unsigned int __writers_futex; + unsigned int __pad3; + unsigned int __pad4; + unsigned char __flags; + unsigned char __shared; + signed char __rwelision; + unsigned char __pad2; + int __cur_writer; +}; +typedef struct __pthread_internal_slist +{ + struct __pthread_internal_slist *__next; +} __pthread_slist_t; +struct __pthread_mutex_s +{ + int __lock ; + unsigned int __count; + int __owner; + int __kind; + + unsigned int __nusers; + __extension__ union + { + struct { short __espins; short __eelision; } __elision_data; + __pthread_slist_t __list; + }; + +}; +struct __pthread_cond_s +{ + __extension__ union + { + __extension__ unsigned long long int __wseq; + struct + { + unsigned int __low; + unsigned int __high; + } __wseq32; + }; + __extension__ union + { + __extension__ unsigned long long int __g1_start; + struct + { + unsigned int __low; + unsigned int __high; + } __g1_start32; + }; + unsigned int __g_refs[2] ; + unsigned int __g_size[2]; + unsigned int __g1_orig_size; + unsigned int __wrefs; + unsigned int __g_signals[2]; +}; +typedef unsigned long int pthread_t; +typedef union +{ + char __size[4]; + int __align; +} pthread_mutexattr_t; +typedef union +{ + char __size[4]; + int __align; +} pthread_condattr_t; +typedef unsigned int pthread_key_t; +typedef int pthread_once_t; +union pthread_attr_t +{ + char __size[36]; + long int __align; +}; +typedef union pthread_attr_t pthread_attr_t; +typedef union +{ + struct __pthread_mutex_s __data; + char __size[24]; + long int __align; +} pthread_mutex_t; +typedef union +{ + struct __pthread_cond_s __data; + char __size[48]; + __extension__ long long int __align; +} pthread_cond_t; +typedef union +{ + struct __pthread_rwlock_arch_t __data; + char __size[32]; + long int __align; +} pthread_rwlock_t; +typedef union +{ + char __size[8]; + long int __align; +} pthread_rwlockattr_t; +typedef volatile int pthread_spinlock_t; +typedef union +{ + char __size[20]; + long int __align; +} pthread_barrier_t; +typedef union +{ + char __size[4]; + int __align; +} pthread_barrierattr_t; +typedef int __jmp_buf[6]; +enum +{ + PTHREAD_CREATE_JOINABLE, + PTHREAD_CREATE_DETACHED +}; +enum +{ + PTHREAD_MUTEX_TIMED_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_ADAPTIVE_NP + , + PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP, + PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; +enum +{ + PTHREAD_MUTEX_STALLED, + PTHREAD_MUTEX_STALLED_NP = PTHREAD_MUTEX_STALLED, + PTHREAD_MUTEX_ROBUST, + PTHREAD_MUTEX_ROBUST_NP = PTHREAD_MUTEX_ROBUST +}; +enum +{ + PTHREAD_PRIO_NONE, + PTHREAD_PRIO_INHERIT, + PTHREAD_PRIO_PROTECT +}; +enum +{ + PTHREAD_RWLOCK_PREFER_READER_NP, + PTHREAD_RWLOCK_PREFER_WRITER_NP, + PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP, + PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_READER_NP +}; +enum +{ + PTHREAD_INHERIT_SCHED, + PTHREAD_EXPLICIT_SCHED +}; +enum +{ + PTHREAD_SCOPE_SYSTEM, + PTHREAD_SCOPE_PROCESS +}; +enum +{ + PTHREAD_PROCESS_PRIVATE, + PTHREAD_PROCESS_SHARED +}; +struct _pthread_cleanup_buffer +{ + void (*__routine) (void *); + void *__arg; + int __canceltype; + struct _pthread_cleanup_buffer *__prev; +}; +enum +{ + PTHREAD_CANCEL_ENABLE, + PTHREAD_CANCEL_DISABLE +}; +enum +{ + PTHREAD_CANCEL_DEFERRED, + PTHREAD_CANCEL_ASYNCHRONOUS +}; + +extern int pthread_create (pthread_t *__restrict __newthread, + const pthread_attr_t *__restrict __attr, + void *(*__start_routine) (void *), + void *__restrict __arg) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 3))); +extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__)); +extern int pthread_join (pthread_t __th, void **__thread_return); +extern int pthread_detach (pthread_t __th) __attribute__ ((__nothrow__ , __leaf__)); +extern pthread_t pthread_self (void) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)); +extern int pthread_equal (pthread_t __thread1, pthread_t __thread2) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__const__)); +extern int pthread_attr_init (pthread_attr_t *__attr) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_destroy (pthread_attr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getdetachstate (const pthread_attr_t *__attr, + int *__detachstate) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setdetachstate (pthread_attr_t *__attr, + int __detachstate) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getguardsize (const pthread_attr_t *__attr, + size_t *__guardsize) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setguardsize (pthread_attr_t *__attr, + size_t __guardsize) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getschedparam (const pthread_attr_t *__restrict __attr, + struct sched_param *__restrict __param) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setschedparam (pthread_attr_t *__restrict __attr, + const struct sched_param *__restrict + __param) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_getschedpolicy (const pthread_attr_t *__restrict + __attr, int *__restrict __policy) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setschedpolicy (pthread_attr_t *__attr, int __policy) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getinheritsched (const pthread_attr_t *__restrict + __attr, int *__restrict __inherit) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setinheritsched (pthread_attr_t *__attr, + int __inherit) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getscope (const pthread_attr_t *__restrict __attr, + int *__restrict __scope) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setscope (pthread_attr_t *__attr, int __scope) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getstackaddr (const pthread_attr_t *__restrict + __attr, void **__restrict __stackaddr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))) __attribute__ ((__deprecated__)); +extern int pthread_attr_setstackaddr (pthread_attr_t *__attr, + void *__stackaddr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))) __attribute__ ((__deprecated__)); +extern int pthread_attr_getstacksize (const pthread_attr_t *__restrict + __attr, size_t *__restrict __stacksize) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_attr_setstacksize (pthread_attr_t *__attr, + size_t __stacksize) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_attr_getstack (const pthread_attr_t *__restrict __attr, + void **__restrict __stackaddr, + size_t *__restrict __stacksize) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2, 3))); +extern int pthread_attr_setstack (pthread_attr_t *__attr, void *__stackaddr, + size_t __stacksize) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_setschedparam (pthread_t __target_thread, int __policy, + const struct sched_param *__param) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (3))); +extern int pthread_getschedparam (pthread_t __target_thread, + int *__restrict __policy, + struct sched_param *__restrict __param) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2, 3))); +extern int pthread_setschedprio (pthread_t __target_thread, int __prio) + __attribute__ ((__nothrow__ , __leaf__)); +extern int pthread_once (pthread_once_t *__once_control, + void (*__init_routine) (void)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_setcancelstate (int __state, int *__oldstate); +extern int pthread_setcanceltype (int __type, int *__oldtype); +extern int pthread_cancel (pthread_t __th); +extern void pthread_testcancel (void); +typedef struct +{ + struct + { + __jmp_buf __cancel_jmp_buf; + int __mask_was_saved; + } __cancel_jmp_buf[1]; + void *__pad[4]; +} __pthread_unwind_buf_t __attribute__ ((__aligned__)); +struct __pthread_cleanup_frame +{ + void (*__cancel_routine) (void *); + void *__cancel_arg; + int __do_it; + int __cancel_type; +}; +extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf) + __attribute__ ((__regparm__ (1))); +extern void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf) + __attribute__ ((__regparm__ (1))); +extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf) + __attribute__ ((__regparm__ (1))) __attribute__ ((__noreturn__)) + __attribute__ ((__weak__)) + ; +struct __jmp_buf_tag; +extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__)); +extern int pthread_mutex_init (pthread_mutex_t *__mutex, + const pthread_mutexattr_t *__mutexattr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutex_destroy (pthread_mutex_t *__mutex) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutex_trylock (pthread_mutex_t *__mutex) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutex_lock (pthread_mutex_t *__mutex) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutex_timedlock (pthread_mutex_t *__restrict __mutex, + const struct timespec *__restrict + __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutex_unlock (pthread_mutex_t *__mutex) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutex_getprioceiling (const pthread_mutex_t * + __restrict __mutex, + int *__restrict __prioceiling) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutex_setprioceiling (pthread_mutex_t *__restrict __mutex, + int __prioceiling, + int *__restrict __old_ceiling) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 3))); +extern int pthread_mutex_consistent (pthread_mutex_t *__mutex) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_init (pthread_mutexattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_destroy (pthread_mutexattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_getpshared (const pthread_mutexattr_t * + __restrict __attr, + int *__restrict __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr, + int __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_gettype (const pthread_mutexattr_t *__restrict + __attr, int *__restrict __kind) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutexattr_settype (pthread_mutexattr_t *__attr, int __kind) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_getprotocol (const pthread_mutexattr_t * + __restrict __attr, + int *__restrict __protocol) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutexattr_setprotocol (pthread_mutexattr_t *__attr, + int __protocol) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_getprioceiling (const pthread_mutexattr_t * + __restrict __attr, + int *__restrict __prioceiling) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutexattr_setprioceiling (pthread_mutexattr_t *__attr, + int __prioceiling) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_mutexattr_getrobust (const pthread_mutexattr_t *__attr, + int *__robustness) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_mutexattr_setrobust (pthread_mutexattr_t *__attr, + int __robustness) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock, + const pthread_rwlockattr_t *__restrict + __attr) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock, + const struct timespec *__restrict + __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock, + const struct timespec *__restrict + __abstime) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * + __restrict __attr, + int *__restrict __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr, + int __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t * + __restrict __attr, + int *__restrict __pref) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *__attr, + int __pref) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_cond_init (pthread_cond_t *__restrict __cond, + const pthread_condattr_t *__restrict __cond_attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_cond_destroy (pthread_cond_t *__cond) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_cond_signal (pthread_cond_t *__cond) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_cond_broadcast (pthread_cond_t *__cond) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_cond_wait (pthread_cond_t *__restrict __cond, + pthread_mutex_t *__restrict __mutex) + __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_cond_timedwait (pthread_cond_t *__restrict __cond, + pthread_mutex_t *__restrict __mutex, + const struct timespec *__restrict __abstime) + __attribute__ ((__nonnull__ (1, 2, 3))); +extern int pthread_condattr_init (pthread_condattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_condattr_destroy (pthread_condattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_condattr_getpshared (const pthread_condattr_t * + __restrict __attr, + int *__restrict __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_condattr_setpshared (pthread_condattr_t *__attr, + int __pshared) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_condattr_getclock (const pthread_condattr_t * + __restrict __attr, + __clockid_t *__restrict __clock_id) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_condattr_setclock (pthread_condattr_t *__attr, + __clockid_t __clock_id) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_spin_init (pthread_spinlock_t *__lock, int __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_spin_destroy (pthread_spinlock_t *__lock) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_spin_lock (pthread_spinlock_t *__lock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_spin_trylock (pthread_spinlock_t *__lock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_spin_unlock (pthread_spinlock_t *__lock) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrier_init (pthread_barrier_t *__restrict __barrier, + const pthread_barrierattr_t *__restrict + __attr, unsigned int __count) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrier_destroy (pthread_barrier_t *__barrier) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrier_wait (pthread_barrier_t *__barrier) + __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrierattr_init (pthread_barrierattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrierattr_destroy (pthread_barrierattr_t *__attr) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_barrierattr_getpshared (const pthread_barrierattr_t * + __restrict __attr, + int *__restrict __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1, 2))); +extern int pthread_barrierattr_setpshared (pthread_barrierattr_t *__attr, + int __pshared) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_key_create (pthread_key_t *__key, + void (*__destr_function) (void *)) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1))); +extern int pthread_key_delete (pthread_key_t __key) __attribute__ ((__nothrow__ , __leaf__)); +extern void *pthread_getspecific (pthread_key_t __key) __attribute__ ((__nothrow__ , __leaf__)); +extern int pthread_setspecific (pthread_key_t __key, + const void *__pointer) __attribute__ ((__nothrow__ , __leaf__)) ; +extern int pthread_getcpuclockid (pthread_t __thread_id, + __clockid_t *__clock_id) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (2))); +extern int pthread_atfork (void (*__prepare) (void), + void (*__parent) (void), + void (*__child) (void)) __attribute__ ((__nothrow__ , __leaf__)); + + +struct _IO_FILE; +typedef struct _IO_FILE __FILE; +struct _IO_FILE; +typedef struct _IO_FILE FILE; +typedef struct +{ + int __count; + union + { + unsigned int __wch; + char __wchb[4]; + } __value; +} __mbstate_t; +typedef struct +{ + __off_t __pos; + __mbstate_t __state; +} _G_fpos_t; +typedef struct +{ + __off64_t __pos; + __mbstate_t __state; +} _G_fpos64_t; +typedef __builtin_va_list __gnuc_va_list; +struct _IO_jump_t; struct _IO_FILE; +typedef void _IO_lock_t; +struct _IO_marker { + struct _IO_marker *_next; + struct _IO_FILE *_sbuf; + int _pos; +}; +enum __codecvt_result +{ + __codecvt_ok, + __codecvt_partial, + __codecvt_error, + __codecvt_noconv +}; +struct _IO_FILE { + int _flags; + char* _IO_read_ptr; + char* _IO_read_end; + char* _IO_read_base; + char* _IO_write_base; + char* _IO_write_ptr; + char* _IO_write_end; + char* _IO_buf_base; + char* _IO_buf_end; + char *_IO_save_base; + char *_IO_backup_base; + char *_IO_save_end; + struct _IO_marker *_markers; + struct _IO_FILE *_chain; + int _fileno; + int _flags2; + __off_t _old_offset; + unsigned short _cur_column; + signed char _vtable_offset; + char _shortbuf[1]; + _IO_lock_t *_lock; + __off64_t _offset; + void *__pad1; + void *__pad2; + void *__pad3; + void *__pad4; + size_t __pad5; + int _mode; + char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)]; +}; +typedef struct _IO_FILE _IO_FILE; +struct _IO_FILE_plus; +extern struct _IO_FILE_plus _IO_2_1_stdin_; +extern struct _IO_FILE_plus _IO_2_1_stdout_; +extern struct _IO_FILE_plus _IO_2_1_stderr_; +typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes); +typedef __ssize_t __io_write_fn (void *__cookie, const char *__buf, + size_t __n); +typedef int __io_seek_fn (void *__cookie, __off64_t *__pos, int __w); +typedef int __io_close_fn (void *__cookie); +extern int __underflow (_IO_FILE *); +extern int __uflow (_IO_FILE *); +extern int __overflow (_IO_FILE *, int); +extern int _IO_getc (_IO_FILE *__fp); +extern int _IO_putc (int __c, _IO_FILE *__fp); +extern int _IO_feof (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); +extern int _IO_ferror (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); +extern int _IO_peekc_locked (_IO_FILE *__fp); +extern void _IO_flockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); +extern void _IO_funlockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); +extern int _IO_ftrylockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); +extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict, + __gnuc_va_list, int *__restrict); +extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict, + __gnuc_va_list); +extern __ssize_t _IO_padn (_IO_FILE *, int, __ssize_t); +extern size_t _IO_sgetn (_IO_FILE *, void *, size_t); +extern __off64_t _IO_seekoff (_IO_FILE *, __off64_t, int, int); +extern __off64_t _IO_seekpos (_IO_FILE *, __off64_t, int); +extern void _IO_free_backup_area (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); +typedef __gnuc_va_list va_list; +typedef __off_t off_t; +typedef __ssize_t ssize_t; +typedef _G_fpos_t fpos_t; +extern struct _IO_FILE *stdin; +extern struct _IO_FILE *stdout; +extern struct _IO_FILE *stderr; +extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__)); +extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__)); +extern int renameat (int __oldfd, const char *__old, int __newfd, + const char *__new) __attribute__ ((__nothrow__ , __leaf__)); +extern FILE *tmpfile (void) ; +extern char *tmpnam (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; +extern char *tmpnam_r (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; +extern char *tempnam (const char *__dir, const char *__pfx) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ; +extern int fclose (FILE *__stream); +extern int fflush (FILE *__stream); +extern int fflush_unlocked (FILE *__stream); +extern FILE *fopen (const char *__restrict __filename, + const char *__restrict __modes) ; +extern FILE *freopen (const char *__restrict __filename, + const char *__restrict __modes, + FILE *__restrict __stream) ; +extern FILE *fdopen (int __fd, const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ; +extern FILE *fmemopen (void *__s, size_t __len, const char *__modes) + __attribute__ ((__nothrow__ , __leaf__)) ; +extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__)) ; +extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)); +extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf, + int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__)); +extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf, + size_t __size) __attribute__ ((__nothrow__ , __leaf__)); +extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); +extern int fprintf (FILE *__restrict __stream, + const char *__restrict __format, ...); +extern int printf (const char *__restrict __format, ...); +extern int sprintf (char *__restrict __s, + const char *__restrict __format, ...) __attribute__ ((__nothrow__)); +extern int vfprintf (FILE *__restrict __s, const char *__restrict __format, + __gnuc_va_list __arg); +extern int vprintf (const char *__restrict __format, __gnuc_va_list __arg); +extern int vsprintf (char *__restrict __s, const char *__restrict __format, + __gnuc_va_list __arg) __attribute__ ((__nothrow__)); +extern int snprintf (char *__restrict __s, size_t __maxlen, + const char *__restrict __format, ...) + __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 4))); +extern int vsnprintf (char *__restrict __s, size_t __maxlen, + const char *__restrict __format, __gnuc_va_list __arg) + __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 0))); +extern int vdprintf (int __fd, const char *__restrict __fmt, + __gnuc_va_list __arg) + __attribute__ ((__format__ (__printf__, 2, 0))); +extern int dprintf (int __fd, const char *__restrict __fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))); +extern int fscanf (FILE *__restrict __stream, + const char *__restrict __format, ...) ; +extern int scanf (const char *__restrict __format, ...) ; +extern int sscanf (const char *__restrict __s, + const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__)); +extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf") ; +extern int scanf (const char *__restrict __format, ...) __asm__ ("" "__isoc99_scanf") ; +extern int sscanf (const char *__restrict __s, const char *__restrict __format, ...) __asm__ ("" "__isoc99_sscanf") __attribute__ ((__nothrow__ , __leaf__)); +extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, + __gnuc_va_list __arg) + __attribute__ ((__format__ (__scanf__, 2, 0))) ; +extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) + __attribute__ ((__format__ (__scanf__, 1, 0))) ; +extern int vsscanf (const char *__restrict __s, + const char *__restrict __format, __gnuc_va_list __arg) + __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0))); +extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vfscanf") + __attribute__ ((__format__ (__scanf__, 2, 0))) ; +extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vscanf") + __attribute__ ((__format__ (__scanf__, 1, 0))) ; +extern int vsscanf (const char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vsscanf") __attribute__ ((__nothrow__ , __leaf__)) + __attribute__ ((__format__ (__scanf__, 2, 0))); +extern int fgetc (FILE *__stream); +extern int getc (FILE *__stream); +extern int getchar (void); +extern int getc_unlocked (FILE *__stream); +extern int getchar_unlocked (void); +extern int fgetc_unlocked (FILE *__stream); +extern int fputc (int __c, FILE *__stream); +extern int putc (int __c, FILE *__stream); +extern int putchar (int __c); +extern int fputc_unlocked (int __c, FILE *__stream); +extern int putc_unlocked (int __c, FILE *__stream); +extern int putchar_unlocked (int __c); +extern int getw (FILE *__stream); +extern int putw (int __w, FILE *__stream); +extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream) + ; +extern __ssize_t __getdelim (char **__restrict __lineptr, + size_t *__restrict __n, int __delimiter, + FILE *__restrict __stream) ; +extern __ssize_t getdelim (char **__restrict __lineptr, + size_t *__restrict __n, int __delimiter, + FILE *__restrict __stream) ; +extern __ssize_t getline (char **__restrict __lineptr, + size_t *__restrict __n, + FILE *__restrict __stream) ; +extern int fputs (const char *__restrict __s, FILE *__restrict __stream); +extern int puts (const char *__s); +extern int ungetc (int __c, FILE *__stream); +extern size_t fread (void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __stream) ; +extern size_t fwrite (const void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __s); +extern size_t fread_unlocked (void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __stream) ; +extern size_t fwrite_unlocked (const void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __stream); +extern int fseek (FILE *__stream, long int __off, int __whence); +extern long int ftell (FILE *__stream) ; +extern void rewind (FILE *__stream); +extern int fseeko (FILE *__stream, __off_t __off, int __whence); +extern __off_t ftello (FILE *__stream) ; +extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos); +extern int fsetpos (FILE *__stream, const fpos_t *__pos); +extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); +extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); +extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern void perror (const char *__s); +extern int sys_nerr; +extern const char *const sys_errlist[]; +extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern FILE *popen (const char *__command, const char *__modes) ; +extern int pclose (FILE *__stream); +extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__)); +extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); +extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; +extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); + +unsigned int __VERIFIER_nondet_uint(); +pthread_mutex_t m; +int arr[1]; + +void *t1(void *arg) +{ + pthread_mutex_lock(&m); + *arr = *arr + 1; + pthread_mutex_unlock(&m); + return 0; +} +int main(void) +{ + *arr = 0; + pthread_t id1; + pthread_mutex_init(&m, 0); + pthread_create(&id1, ((void *)0), t1, ((void *)0)); + pthread_mutex_lock(&m); + *arr = *arr + 1; + pthread_mutex_unlock(&m); + pthread_join(id1, ((void *)0)); + if(*arr != 2) { + reach_error(); + } + return 0; +} diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/27postfix.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/27postfix.c new file mode 100644 index 0000000000..12d0896b1e --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/27postfix.c @@ -0,0 +1,48 @@ +void reach_error(){} + +struct A{ + int a; + char c; + struct { + int a; + } b; +}; + +struct A structs[1]; + +void f(struct A* a_ptr) { + a_ptr->a = structs[0].a + 1; // this is .a++ +} + +void g(struct A a_val) { + a_val.a = structs[0].a + 1; // this is .a++, but local only +} + +struct A* h() { + return structs; +} + +int main() { + if(structs[0].a + structs[0].c + structs[0].b.a != 0) { + reach_error(); // global vars should be initialized to 0, even nested ones + } + f(&(structs[0])); + if(structs[0].a != 1) { + reach_error(); // .a++ in f() + } + g(structs[0]); + if(structs[0].a != 1) { + reach_error(); // .a++ in g() is local-only + } + g(structs[0]); + if(structs->a != 1) { // structs->a should in theory work + reach_error(); // .a++ in g() is still local-only + } + g(structs[0]); + if(h()[0].a != 1) { // h just returns structs + reach_error(); // .a++ in g() is still local-only + } + if(h()[0].a++ != 2) { // most beautiful postfix expression + reach_error(); + } +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/28multidim.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/28multidim.c new file mode 100644 index 0000000000..155ca354f8 --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/28multidim.c @@ -0,0 +1,15 @@ +void reach_error(){} + +int a[2] = {0, 0}; +int b[2] = {0, 0}; + +int main() { + int k; + if(k >= 0 && k < 2) { + a[k] = 1; + if(b[k] == 1) { + reach_error(); + } + } + +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/29whilepp.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/29whilepp.c new file mode 100644 index 0000000000..b24eebd909 --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/29whilepp.c @@ -0,0 +1,8 @@ +void reach_error(){} + +int main() { + int i = 0; + while(i++<1) { reach_error(); } + + +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/30hardptr.c b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/30hardptr.c new file mode 100644 index 0000000000..28a18af92c --- /dev/null +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/c/litmustest/singlethread/30hardptr.c @@ -0,0 +1,12 @@ +void reach_error(){} +extern int* malloc(); +int main() { + int *i = malloc(); + int x, y; + + i[x+2] = 1; + i[y-2] = 2; + if(i[x+2] == 1 && x+2==y-2) { + reach_error(); + } +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts b/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts index 528bfe1432..fb98c7344d 100644 --- a/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts +++ b/subprojects/xcfa/xcfa-cli/src/test/resources/simple.kts @@ -36,7 +36,7 @@ fun portfolio( logger: Logger, uniqueLogger: Logger): STM { - val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger) } + val checker = { config: XcfaConfig<*, *> -> runConfig(config, logger, uniqueLogger, true) } var baseConfig = XcfaConfig( inputConfig = InputConfig( diff --git a/subprojects/xcfa/xcfa/build.gradle.kts b/subprojects/xcfa/xcfa/build.gradle.kts index ed980572ed..49c1ff536f 100644 --- a/subprojects/xcfa/xcfa/build.gradle.kts +++ b/subprojects/xcfa/xcfa/build.gradle.kts @@ -24,5 +24,5 @@ dependencies { implementation(project(":theta-c-frontend")) implementation(project(":theta-analysis")) implementation(project(":theta-solver")) - implementation(project(mapOf("path" to ":theta-solver-z3-legacy"))) + implementation(project(":theta-solver-z3")) } 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 beb6d99728..cc6f24571b 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 @@ -16,17 +16,32 @@ package hu.bme.mit.theta.xcfa +import com.google.common.base.Preconditions.checkState +import hu.bme.mit.theta.common.Try import hu.bme.mit.theta.common.dsl.Env import hu.bme.mit.theta.common.dsl.Symbol import hu.bme.mit.theta.common.dsl.SymbolTable 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.model.MutableValuation +import hu.bme.mit.theta.core.model.Valuation +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.LitExpr +import hu.bme.mit.theta.core.type.NullaryExpr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.abstracttype.ModExpr +import hu.bme.mit.theta.core.type.abstracttype.NeqExpr +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.anytype.Reference import hu.bme.mit.theta.core.type.booltype.BoolType import hu.bme.mit.theta.core.utils.ExprUtils +import hu.bme.mit.theta.core.utils.StmtSimplifier import hu.bme.mit.theta.core.utils.StmtUtils +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.xcfa.model.* import java.util.function.Predicate @@ -97,7 +112,7 @@ fun XcfaLabel.collectVars(): Iterable> = when (this) { // Complex var access requests typealias AccessType = Pair -private typealias VarAccessMap = Map, AccessType> +typealias VarAccessMap = Map, AccessType> val AccessType?.isRead get() = this?.first == true val AccessType?.isWritten get() = this?.second == true @@ -114,31 +129,56 @@ private fun List.mergeAndCollect(): VarAccessMap = this.fold(mapOf private operator fun VarAccessMap?.plus(other: VarAccessMap?): VarAccessMap = 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 + /** - * The list of mutexes acquired by the label. + * The set of mutexes acquired by the label. */ -inline val FenceLabel.acquiredMutexes: Set - get() = labels.mapNotNull { - when { - it == "ATOMIC_BEGIN" -> "" - it.startsWith("mutex_lock") -> it.substringAfter('(').substringBefore(')') - it.startsWith("cond_wait") -> it.substring("cond_wait".length + 1, it.length - 1).split(",")[1] - else -> null - } - }.toSet() +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 + } + +/** + * 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 + } /** - * The list of mutexes released by the label. + * The set of mutexes acquired embedded into each other. */ -inline val FenceLabel.releasedMutexes: Set - get() = labels.mapNotNull { - when { - it == "ATOMIC_END" -> "" - it.startsWith("mutex_unlock") -> it.substringAfter('(').substringBefore(')') - it.startsWith("start_cond_wait") -> it.substring("start_cond_wait".length + 1, it.length - 1).split(",")[1] - else -> null +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) } + } } - }.toSet() + return acquired + } /** * Returns the list of accessed variables by the edge associated with an AccessType object. @@ -154,13 +194,25 @@ fun XcfaLabel.collectVarsWithAccessType(): VarAccessMap = when (this) { 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 } } } is NondetLabel -> labels.map { it.collectVarsWithAccessType() }.mergeAndCollect() is SequenceLabel -> labels.map { it.collectVarsWithAccessType() }.mergeAndCollect() - is InvokeLabel -> params.map { ExprUtils.getVars(it) }.flatten().associateWith { 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) @@ -309,10 +361,10 @@ private fun getAtomicBlockInnerLocations(initialLocation: XcfaLocation): 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() + } + + 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 } + } else { + 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() + } + +val Stmt.dereferences: List> + 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) + } else { + 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() + } + +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() + } + +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) + } + + else -> this + } +} else this + +data class MallocLitExpr(val kType: T) : NullaryExpr(), LitExpr { + + 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<*> + } + .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()) }) + } + } + + 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) \ No newline at end of file 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 e3247473c6..6eacb8f086 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 @@ -20,6 +20,7 @@ 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.* import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Geq import hu.bme.mit.theta.core.type.abstracttype.AbstractExprs.Leq @@ -27,6 +28,7 @@ import hu.bme.mit.theta.core.type.abstracttype.DivExpr import hu.bme.mit.theta.core.type.abstracttype.EqExpr import hu.bme.mit.theta.core.type.abstracttype.ModExpr import hu.bme.mit.theta.core.type.abstracttype.NeqExpr +import hu.bme.mit.theta.core.type.anytype.Dereference import hu.bme.mit.theta.core.type.anytype.IteExpr import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.type.arraytype.ArrayReadExpr @@ -58,6 +60,8 @@ private const val arraySize = 10; 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(); extern int __VERIFIER_nondet_int(); extern _Bool __VERIFIER_nondet__Bool(); extern void reach_error(); @@ -263,6 +267,7 @@ private fun StmtLabel.toC(parseContext: ParseContext, intRangeConstraint: Boolea }(); ${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") } @@ -294,10 +299,13 @@ fun Expr<*>.toC(parseContext: 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)})" 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 beef7f20f2..eeebe05881 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 @@ -83,7 +83,8 @@ class XcfaLabelAdapter(val scope: Scope, val env: Env, val gsonSupplier: () -> G constructor.call(clazz.companionObject!!.objectInstance, content, scope, env, metadata) } catch (e: Exception) { - System.err.println("Could not parse $content\nscope: ${scope}\nenv: $env") + System.err.println( + "Could not parse $content\nscope: ${scope}\nenv: $env\ntype: ${clazz.simpleName}") throw e } check(obj is XcfaLabel) 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 604a0497b1..bf8d3e3730 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 @@ -18,6 +18,7 @@ package hu.bme.mit.theta.xcfa.model import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.Type import hu.bme.mit.theta.xcfa.passes.ProcedurePassManager import java.util.* @@ -28,6 +29,7 @@ annotation class XcfaDsl class XcfaBuilder @JvmOverloads constructor( var name: String, private val vars: MutableSet = LinkedHashSet(), + val heapMap: MutableMap, VarDecl<*>> = LinkedHashMap(), private val procedures: MutableSet = LinkedHashSet(), private val initProcedures: MutableList>>> = ArrayList(), val metaData: MutableMap = LinkedHashMap() @@ -193,6 +195,13 @@ class XcfaProcedureBuilder @JvmOverloads constructor( } } + fun copyMetaLocs(from: XcfaProcedureBuilder) { + check(!this::optimized.isInitialized) { "Cannot add/remove new elements after optimization passes!" } + initLoc = from.initLoc + finalLoc = from.finalLoc + errorLoc = from.errorLoc + } + fun addEdge(toAdd: XcfaEdge) { check(!this::optimized.isInitialized) { "Cannot add/remove new elements after optimization passes!" } addLoc(toAdd.source) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Dsl.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Dsl.kt index 9d4558df9e..b146c7d4f9 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Dsl.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/Dsl.kt @@ -114,14 +114,14 @@ class XcfaProcedureBuilderContext(val builder: XcfaProcedureBuilder) { val lhs: VarDecl = this@XcfaProcedureBuilderContext.builder.lookup( this) as VarDecl val rhs: Expr = this@XcfaProcedureBuilderContext.builder.parse(to) as Expr - val label = StmtLabel(Assign(lhs, rhs), metadata = EmptyMetaData) + val label = StmtLabel(Assign(lhs, rhs)) labelList.add(label) return SequenceLabel(labelList) } infix fun VarDecl<*>.assign(to: String): SequenceLabel { val rhs: Expr = this@XcfaProcedureBuilderContext.builder.parse(to) as Expr - val label = StmtLabel(Assign(this as VarDecl, rhs), metadata = EmptyMetaData) + val label = StmtLabel(Assign(this as VarDecl, rhs)) labelList.add(label) return SequenceLabel(labelList) } @@ -130,40 +130,40 @@ class XcfaProcedureBuilderContext(val builder: XcfaProcedureBuilder) { val lhs: VarDecl = this@XcfaProcedureBuilderContext.builder.lookup( this) as VarDecl val rhs: Expr = to as Expr - val label = StmtLabel(Assign(lhs, rhs), metadata = EmptyMetaData) + val label = StmtLabel(Assign(lhs, rhs)) labelList.add(label) return SequenceLabel(labelList) } infix fun VarDecl<*>.assign(to: Expr<*>): SequenceLabel { val rhs: Expr = to as Expr - val label = StmtLabel(Assign(this as VarDecl, rhs), metadata = EmptyMetaData) + val label = StmtLabel(Assign(this as VarDecl, rhs)) labelList.add(label) return SequenceLabel(labelList) } fun assume(value: String): SequenceLabel { val expr = this@XcfaProcedureBuilderContext.builder.parse(value) as Expr - val label = StmtLabel(Assume(expr), metadata = EmptyMetaData) + val label = StmtLabel(Assume(expr)) labelList.add(label) return SequenceLabel(labelList) } fun assume(expr: Expr): SequenceLabel { - val label = StmtLabel(Assume(expr), metadata = EmptyMetaData) + val label = StmtLabel(Assume(expr)) labelList.add(label) return SequenceLabel(labelList) } fun havoc(value: String): SequenceLabel { val varDecl = this@XcfaProcedureBuilderContext.builder.lookup(value) - val label = StmtLabel(Havoc(varDecl), metadata = EmptyMetaData) + val label = StmtLabel(Havoc(varDecl)) labelList.add(label) return SequenceLabel(labelList) } fun havoc(varDecl: VarDecl<*>): SequenceLabel { - val label = StmtLabel(Havoc(varDecl), metadata = EmptyMetaData) + val label = StmtLabel(Havoc(varDecl)) labelList.add(label) return SequenceLabel(labelList) } 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 f6d18f65c3..177c75758d 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 @@ -16,15 +16,14 @@ package hu.bme.mit.theta.xcfa.model -fun XCFA.toDot(): String { +fun XCFA.toDot(edgeLabelCustomizer: ((XcfaEdge) -> String)? = null): String { val builder = StringBuilder() builder.appendLine("digraph G {") builder.appendLine("label=\"$name\";") - var i = 0 - for (procedure in procedures) { - builder.appendLine("subgraph cluster_" + i++ + " {") - builder.appendLine(procedure.toDot()) + for ((i, procedure) in procedures.withIndex()) { + builder.appendLine("subgraph cluster_$i {") + builder.appendLine(procedure.toDot(edgeLabelCustomizer)) builder.appendLine("}") } @@ -32,13 +31,13 @@ fun XCFA.toDot(): String { return builder.toString() } -fun XcfaProcedure.toDot(): String { +fun XcfaProcedure.toDot(edgeLabelCustomizer: ((XcfaEdge) -> String)?): String { val builder = StringBuilder() builder.appendLine("label=\"$name\";") - locs.forEach { builder.appendLine(it.name + "[];") } + locs.forEach { builder.appendLine("${it.name}[];") } edges.forEach { builder.appendLine( - it.source.name + " -> " + it.target.name + "[label=\"" + it.label.toString() + "\"];") + "${it.source.name} -> ${it.target.name} [label=\"${it.label} ${edgeLabelCustomizer?.invoke(it) ?: ""}\"];") } return builder.toString() } \ No newline at end of file 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 ddc16f3c17..ba90dcdfdf 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 @@ -19,15 +19,18 @@ package hu.bme.mit.theta.xcfa.model import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.LitExpr +import hu.bme.mit.theta.xcfa.lazyPointsToGraph import java.util.* class XCFA( val name: String, val vars: Set, // global variables - procedureBuilders: Set = emptySet(), - initProcedureBuilders: List>>> = emptyList() + val procedureBuilders: Set = emptySet(), + val initProcedureBuilders: List>>> = emptyList() ) { + val pointsToGraph by this.lazyPointsToGraph + var cachedHash: Int? = null var procedures: Set // procedure definitions @@ -117,10 +120,8 @@ data class XcfaLocation @JvmOverloads constructor( companion object { - private var cnt: Int = 0; - fun uniqueCounter(): Int { - return cnt++; - } + private var cnt: Int = 0 + fun uniqueCounter(): Int = cnt++ } override fun toString(): String { diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaLabel.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaLabel.kt index d5c890b077..93f636b306 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaLabel.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/model/XcfaLabel.kt @@ -125,7 +125,7 @@ enum class ChoiceType { data class StmtLabel @JvmOverloads constructor( val stmt: Stmt, val choiceType: ChoiceType = ChoiceType.NONE, - override val metadata: MetaData + override val metadata: MetaData = EmptyMetaData ) : XcfaLabel(metadata = metadata) { init { @@ -183,7 +183,7 @@ data class WriteLabel constructor( data class FenceLabel( val labels: Set, - override val metadata: MetaData + override val metadata: MetaData = EmptyMetaData ) : XcfaLabel(metadata = metadata) { override fun toString(): String { 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 new file mode 100644 index 0000000000..17bbdde26e --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AssumeFalseRemovalPass.kt @@ -0,0 +1,50 @@ +/* + * 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.passes + +import hu.bme.mit.theta.core.stmt.AssumeStmt +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. + */ +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) + } + } + + 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 new file mode 100644 index 0000000000..d4927ebaad --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/AtomicReadsOneWritePass.kt @@ -0,0 +1,182 @@ +/* + * 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.passes + +import hu.bme.mit.theta.core.decl.Decl +import hu.bme.mit.theta.core.decl.Decls +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.type.Expr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.RefExpr +import hu.bme.mit.theta.core.utils.TypeUtils.cast +import hu.bme.mit.theta.core.utils.indexings.VarIndexing +import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory +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. + */ +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))) + } + } + } + + 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))) + } + } + 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 + } + + 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!! + } + + 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) } +} 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 bbc76e4ae4..7803f1daaf 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 @@ -19,16 +19,15 @@ package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.VarDecl 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.frontend.ParseContext -import hu.bme.mit.theta.frontend.transformation.grammar.expression.Reference import hu.bme.mit.theta.xcfa.model.* /** * Transforms the library procedure calls with names in supportedFunctions into model elements. * Requires the ProcedureBuilder be `deterministic`. */ -class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { +class CLibraryFunctionsPass : ProcedurePass { private val supportedFunctions = setOf( "printf", @@ -56,7 +55,7 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "printf" -> listOf(NopLabel) "pthread_join" -> { var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.op + 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)) @@ -64,7 +63,7 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "pthread_create" -> { var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.op + while (handle is Reference<*, *>) handle = handle.expr check(handle is RefExpr && (handle as RefExpr).decl is VarDecl) val funcptr = invokeLabel.params[3] @@ -79,7 +78,7 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "pthread_mutex_lock" -> { var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.op + 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)) @@ -87,7 +86,7 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "pthread_mutex_unlock" -> { var handle = invokeLabel.params[1] - while (handle is Reference<*, *>) handle = handle.op + 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)) @@ -95,9 +94,9 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "pthread_cond_wait" -> { var cond = invokeLabel.params[1] - while (cond is Reference<*, *>) cond = cond.op + while (cond is Reference<*, *>) cond = cond.expr var handle = invokeLabel.params[2] - while (handle is Reference<*, *>) handle = handle.op + 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) @@ -110,7 +109,7 @@ class CLibraryFunctionsPass(val parseContext: ParseContext) : ProcedurePass { "pthread_cond_signal" -> { var cond = invokeLabel.params[1] - while (cond is Reference<*, *>) cond = cond.op + 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)) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/DeterministicPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/DeterministicPass.kt index ef3e70b7b8..1f4b1ab032 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/DeterministicPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/DeterministicPass.kt @@ -16,7 +16,6 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.NondetLabel import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder @@ -26,7 +25,7 @@ import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder * Sets the `deterministic` flag on the ProcedureBuilder */ -class DeterministicPass(val parseContext: ParseContext) : ProcedurePass { +class DeterministicPass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["normal"]) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EliminateSelfLoops.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EliminateSelfLoops.kt index 72c97ba379..0c5c4c461d 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EliminateSelfLoops.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EliminateSelfLoops.kt @@ -15,14 +15,10 @@ */ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext -import hu.bme.mit.theta.xcfa.model.NopLabel -import hu.bme.mit.theta.xcfa.model.XcfaEdge -import hu.bme.mit.theta.xcfa.model.XcfaLocation -import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder +import hu.bme.mit.theta.xcfa.model.* import java.util.stream.Collectors -class EliminateSelfLoops(val parseContext: ParseContext) : ProcedurePass { +class EliminateSelfLoops : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { val selfLoops: Set = builder.getEdges().stream() @@ -30,15 +26,14 @@ class EliminateSelfLoops(val parseContext: ParseContext) : ProcedurePass { for (selfLoop in selfLoops) { builder.removeEdge(selfLoop) val source = selfLoop.source - val target: XcfaLocation = XcfaLocation( - source.name + "_" + XcfaLocation.uniqueCounter()) + val target = XcfaLocation(source.name + "_" + XcfaLocation.uniqueCounter()) builder.addLoc(target) for (outgoingEdge in LinkedHashSet(source.outgoingEdges)) { builder.removeEdge(outgoingEdge) builder.addEdge(XcfaEdge(target, outgoingEdge.target, outgoingEdge.label)) } builder.addEdge(XcfaEdge(source, target, selfLoop.label)) - builder.addEdge(XcfaEdge(target, source, NopLabel)) + builder.addEdge(XcfaEdge(target, source, SequenceLabel(listOf(NopLabel)))) } builder.metaData["noSelfLoops"] = Unit return builder diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EmptyEdgeRemovalPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EmptyEdgeRemovalPass.kt index 83ab54314f..90e7170707 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EmptyEdgeRemovalPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/EmptyEdgeRemovalPass.kt @@ -16,25 +16,34 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext +import hu.bme.mit.theta.core.stmt.Stmts.Assume +import hu.bme.mit.theta.core.type.booltype.BoolExprs.True import hu.bme.mit.theta.xcfa.model.* /** * Removes edges that only contain NopLabels (possibly nested) */ -class EmptyEdgeRemovalPass(val parseContext: ParseContext) : ProcedurePass { +class EmptyEdgeRemovalPass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { while (true) { - val edge = builder.getEdges() - .find { it.label.isNop() && !it.target.error && !it.target.final && !it.source.initial } - ?: return builder + val edge = builder.getEdges().find { + it.label.isNop() && !it.target.error && !it.target.final && !it.source.initial && + (it.source.outgoingEdges.size == 1 || it.target.incomingEdges.size == 1) + } ?: return builder + val collapseBefore = edge.source.outgoingEdges.size == 1 builder.removeEdge(edge) - if (edge.source != edge.target) { - val incomingEdges = ArrayList(edge.source.incomingEdges) + if (collapseBefore) { + val incomingEdges = edge.source.incomingEdges.toList() incomingEdges.forEach { builder.removeEdge(it) } incomingEdges.forEach { builder.addEdge(it.withTarget(edge.target)) } + builder.removeLoc(edge.source) + } else { + val outgoingEdges = edge.target.outgoingEdges.toList() + outgoingEdges.forEach { builder.removeEdge(it) } + outgoingEdges.forEach { builder.addEdge(it.withSource(edge.source)) } + builder.removeLoc(edge.target) } } } @@ -44,6 +53,7 @@ class EmptyEdgeRemovalPass(val parseContext: ParseContext) : ProcedurePass { is NondetLabel -> labels.all { it.isNop() } is SequenceLabel -> labels.all { it.isNop() } is NopLabel -> true + is StmtLabel -> stmt == Assume(True()) else -> false } } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ErrorLocationPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ErrorLocationPass.kt index 6d91dbf262..d539cb6240 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ErrorLocationPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ErrorLocationPass.kt @@ -16,14 +16,13 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.* /** * Transforms all procedure calls to designated error procedures into edges to error locations. * Requires the ProcedureBuilder be `deterministic`. */ -class ErrorLocationPass(val checkOverflow: Boolean, val parseContext: ParseContext) : ProcedurePass { +class ErrorLocationPass(private val checkOverflow: Boolean) : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) @@ -47,6 +46,6 @@ class ErrorLocationPass(val checkOverflow: Boolean, val parseContext: ParseConte } private fun predicate(it: XcfaLabel): Boolean { - return it is InvokeLabel && it.name.equals(if (checkOverflow) "overflow" else "reach_error") + return it is InvokeLabel && it.name == if (checkOverflow) "overflow" else "reach_error" } } \ No newline at end of file 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 new file mode 100644 index 0000000000..0ed1eee7a1 --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FetchExecuteWriteback.kt @@ -0,0 +1,140 @@ +/* + * 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.passes + +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.AssumeStmt +import hu.bme.mit.theta.core.stmt.MemoryAssignStmt +import hu.bme.mit.theta.core.stmt.Stmt +import hu.bme.mit.theta.core.stmt.Stmts.Assign +import hu.bme.mit.theta.core.stmt.Stmts.MemoryAssign +import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.Dereference +import hu.bme.mit.theta.core.type.booltype.BoolType +import hu.bme.mit.theta.core.utils.TypeUtils.cast +import hu.bme.mit.theta.frontend.ParseContext +import hu.bme.mit.theta.xcfa.* +import hu.bme.mit.theta.xcfa.model.* + +/** + * Transforms derefs into variables if possible (in the entire XCFA, no derefs remain non-literal) + * Requires the ProcedureBuilder be `deterministic`. + */ +class FetchExecuteWriteback(val parseContext: ParseContext) : ProcedurePass { + + companion object { + + var enabled = false + + private var cnt = 0 + get() = field++ + } + + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + if (!enabled) return builder + checkNotNull(builder.metaData["deterministic"]) + val localDerefs = builder.getEdges().flatMap { it.getFlatLabels().flatMap { it.dereferences } } + + 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 DeterministicPass().run(NormalizePass().run(builder)) + } + + 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) + } + + 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))) + } + 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) }) + } +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FinalLocationPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FinalLocationPass.kt index b778e58732..987722b622 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FinalLocationPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FinalLocationPass.kt @@ -18,14 +18,13 @@ package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.stmt.Stmts import hu.bme.mit.theta.core.type.booltype.BoolExprs -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.* /** * Transforms all procedure calls to designated `abort` procedures into edges to final locations. * Requires the ProcedureBuilder be `deterministic`. */ -class FinalLocationPass(val checkOverflow: Boolean, val parseContext: ParseContext) : ProcedurePass { +class FinalLocationPass(private val checkOverflow: Boolean) : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FpFunctionsToExprsPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FpFunctionsToExprsPass.kt index 3afee0715f..8a05f0ef2b 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FpFunctionsToExprsPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/FpFunctionsToExprsPass.kt @@ -23,6 +23,7 @@ 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 import hu.bme.mit.theta.core.type.anytype.RefExpr +import hu.bme.mit.theta.core.type.booltype.BoolExprs.Or import hu.bme.mit.theta.core.type.fptype.* import hu.bme.mit.theta.core.utils.TypeUtils import hu.bme.mit.theta.frontend.ParseContext @@ -92,7 +93,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { "roundl")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> handleRound(builder, callStmt) } - addHandler(arrayOf("isnan")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> + addHandler(arrayOf("isnan", "__isnan", "isnanf", "__isnanf", "isnanl", + "__isnanl")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> handleIsnan(builder, callStmt) } addHandler(arrayOf("trunc")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> @@ -105,12 +107,14 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { "isnormal")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> handleIsnormal(builder, callStmt) } - addHandler(arrayOf("isinf", "__isinf", "__isinff", - "__isinfl")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> + addHandler(arrayOf("isinf", "__isinf", "__isinff", "isinff", + "__isinfl", "isinfl")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> handleIsinf(builder, callStmt) } addHandler(arrayOf( - "isfinite")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> + "isfinite", "finite", "isfinitef", "finitef", "isfinite", "finitel", "__finite", "__finitef", "__finitel", + "__isfinite", + "__isfinitef", "__isfinitel")) { builder: XcfaProcedureBuilder, callStmt: InvokeLabel -> handleIsfinite(builder, callStmt) } addHandler(arrayOf("__fpclassify", "__fpclassifyf", @@ -127,8 +131,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { FpRoundToIntegralExpr.of(FpRoundingMode.RTZ, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -141,8 +145,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { FpRoundToIntegralExpr.of(FpRoundingMode.RTP, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -171,7 +175,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { val assign: AssignStmt<*> = Stmts.Assign( TypeUtils.cast((expr as RefExpr<*>).decl as VarDecl<*>, type.smtType), TypeUtils.cast(AbstractExprs.Ite( - FpIsInfiniteExpr.of(callStmt.params[1] as Expr), + Or(FpIsInfiniteExpr.of(callStmt.params[1] as Expr), + FpIsNanExpr.of(callStmt.params[1] as Expr)), type.nullValue, type.unitValue), type.smtType)) parseContext.metadata.create(assign.expr, "cType", type) @@ -192,7 +197,7 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { Preconditions.checkState(callStmt.params.size == 2, "Function is presumed to be unary!") val expr = callStmt.params[0] Preconditions.checkState(expr is RefExpr<*>) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { val type = CComplexType.getType(expr, parseContext) val assign: AssignStmt<*> = Stmts.Assign( TypeUtils.cast((expr as RefExpr<*>).decl as VarDecl<*>, type.smtType), @@ -200,7 +205,7 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { AbstractExprs.Ite( FpIsNanExpr.of(callStmt.params[1] as Expr), type.unitValue, type.nullValue), type.smtType)) - parseContext.getMetadata().create(assign.expr, "cType", type) + parseContext.metadata.create(assign.expr, "cType", type) return StmtLabel(assign, metadata = callStmt.metadata) } else { throw UnsupportedOperationException("Not yet supported without cType") @@ -215,8 +220,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { FpRoundToIntegralExpr.of(FpRoundingMode.RNA, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -229,8 +234,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { FpSqrtExpr.of(FpRoundingMode.RNE, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -249,8 +254,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { CComplexType.getType(expr, parseContext).smtType) as Expr, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[2]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -265,8 +270,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { CComplexType.getType(expr, parseContext).smtType) as Expr, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[2]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -279,8 +284,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { FpRoundToIntegralExpr.of(FpRoundingMode.RTN, TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } @@ -292,8 +297,8 @@ class FpFunctionsToExprsPass(val parseContext: ParseContext) : ProcedurePass { val assign = Stmts.Assign((expr as RefExpr<*>).decl as VarDecl, FpAbsExpr.of(TypeUtils.cast(CComplexType.getType(expr, parseContext).castTo(callStmt.params[1]), CComplexType.getType(expr, parseContext).smtType) as Expr)) - if (parseContext.getMetadata().getMetadataValue(expr, "cType").isPresent) { - parseContext.getMetadata().create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) + if (parseContext.metadata.getMetadataValue(expr, "cType").isPresent) { + parseContext.metadata.create(assign.expr, "cType", CComplexType.getType(expr, parseContext)) } return StmtLabel(assign, metadata = callStmt.metadata) } 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 e5703b589d..7b0b3eccfa 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 @@ -67,7 +67,7 @@ class HavocPromotionAndRange(val parseContext: ParseContext) : ProcedurePass { if (indices.isNotEmpty()) { builder.removeEdge(edge) val newLabels = ArrayList() - var offset = 0; + 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]) { @@ -76,7 +76,7 @@ class HavocPromotionAndRange(val parseContext: ParseContext) : ProcedurePass { val havoc = Havoc(varDecl) newLabels.add( StmtLabel(havoc, metadata = edge.label.labels[index].metadata)) -// newLabels.add(StmtLabel(type.limit(varDecl.ref), metadata = EmptyMetaData)) +// newLabels.add(StmtLabel(type.limit(varDecl.ref))) } else if (index == indices[offset] + 1) { offset++ } else { diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/InlineProceduresPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/InlineProceduresPass.kt index a33afdefc7..48114fc020 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/InlineProceduresPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/InlineProceduresPass.kt @@ -45,12 +45,12 @@ class InlineProceduresPass(val parseContext: ParseContext) : ProcedurePass { val edges = edge.splitIf(pred) if (edges.size > 1 || (edges.size == 1 && pred((edges[0].label as SequenceLabel).labels[0]))) { builder.removeEdge(edge) - edges.forEach { - if (pred((it.label as SequenceLabel).labels[0])) { + edges.forEach { e -> + if (pred((e.label as SequenceLabel).labels[0])) { foundOne = true - val source = it.source - val target = it.target - val invokeLabel: InvokeLabel = it.label.labels[0] as InvokeLabel + val source = e.source + val target = e.target + val invokeLabel: InvokeLabel = e.label.labels[0] as InvokeLabel val procedure = builder.parent.getProcedures().find { p -> p.name == invokeLabel.name } checkNotNull(procedure) val inlineIndex = builder.manager.passes.indexOfFirst { phase -> @@ -59,7 +59,7 @@ class InlineProceduresPass(val parseContext: ParseContext) : ProcedurePass { procedure.optimize(inlineIndex) val newLocs: MutableMap = LinkedHashMap() - procedure.getLocs().forEach { newLocs.put(it, it.inlinedCopy()) } + procedure.getLocs().forEach { newLocs[it] = it.inlinedCopy() } procedure.getVars().forEach { builder.addVar(it) } procedure.getParams().forEach { builder.addVar(it.first) } procedure.getEdges().forEach { @@ -101,7 +101,7 @@ class InlineProceduresPass(val parseContext: ParseContext) : ProcedurePass { SequenceLabel(listOf()))) } } else { - builder.addEdge(it) + builder.addEdge(e) } } @@ -114,6 +114,6 @@ class InlineProceduresPass(val parseContext: ParseContext) : ProcedurePass { } private fun XcfaLocation.inlinedCopy(): XcfaLocation { - return copy(name + XcfaLocation.uniqueCounter(), initial = false, final = false, error = false) + return copy(name = name + "_" + XcfaLocation.uniqueCounter(), initial = false, final = false, error = false) } } \ No newline at end of file diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/LoopUnrollPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/LoopUnrollPass.kt index 64e1e48cd6..2a2fc5ee60 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/LoopUnrollPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/LoopUnrollPass.kt @@ -24,7 +24,7 @@ import hu.bme.mit.theta.core.model.ImmutableValuation import hu.bme.mit.theta.core.stmt.AssumeStmt import hu.bme.mit.theta.core.stmt.Stmt import hu.bme.mit.theta.solver.Solver -import hu.bme.mit.theta.solver.z3legacy.Z3LegacySolverFactory +import hu.bme.mit.theta.solver.z3.Z3SolverFactory import hu.bme.mit.theta.xcfa.collectVars import hu.bme.mit.theta.xcfa.collectVarsWithAccessType import hu.bme.mit.theta.xcfa.getFlatLabels @@ -35,23 +35,27 @@ import java.util.* /** * Unrolls loops where the number of loop executions can be determined statically. * The UNROLL_LIMIT refers to the number of loop executions: loops that are executed more times than this limit - * are not unrolled. + * are not unrolled. Loops with unknown number of iterations are unrolled to FORCE_UNROLL_LIMIT iterations (this + * way a safe result might not be valid). */ class LoopUnrollPass : ProcedurePass { companion object { - var UNROLL_LIMIT = 50 + var UNROLL_LIMIT = 1000 + var FORCE_UNROLL_LIMIT = -1 + var FORCE_UNROLL_USED = false - private val solver: Solver = Z3LegacySolverFactory.getInstance().createSolver() + private val solver: Solver = Z3SolverFactory.getInstance().createSolver() } private val testedLoops = mutableSetOf() private data class Loop( - val loopVar: VarDecl<*>, val loopVarModifiers: List, val loopVarInit: XcfaEdge, - val loopCondEdge: XcfaEdge, val exitCondEdge: XcfaEdge, val loopStart: XcfaLocation, - val loopLocs: List, val loopEdges: List + val loopStart: XcfaLocation, val loopLocs: Set, val loopEdges: Set, + val loopVar: VarDecl<*>?, val loopVarInit: XcfaEdge?, val loopVarModifiers: List?, + val loopStartEdges: List, val exitEdges: Map>, + val properlyUnrollable: Boolean ) { private class BasicStmtAction(private val stmt: Stmt) : StmtAction() { @@ -61,73 +65,85 @@ class LoopUnrollPass : ProcedurePass { override fun getStmts() = listOf(stmt) } - private fun count(transFunc: ExplStmtTransFunc): Int { + fun unroll(builder: XcfaProcedureBuilder, transFunc: ExplStmtTransFunc) { + val count = count(transFunc) + if (count != null) { + unroll(builder, count, true) + } else if (FORCE_UNROLL_LIMIT != -1) { + FORCE_UNROLL_USED = true + unroll(builder, FORCE_UNROLL_LIMIT, false) + } + } + + fun unroll(builder: XcfaProcedureBuilder, count: Int, removeCond: Boolean) { + (loopLocs - loopStart).forEach(builder::removeLoc) + loopLocs.flatMap { it.outgoingEdges }.forEach(builder::removeEdge) + + var startLocation = loopStart + for (i in 0 until count) { + startLocation = copyBody(builder, startLocation, i, removeCond) + } + + exitEdges[loopStart]?.forEach { edge -> + val label = if (removeCond) edge.label.removeCondition() else edge.label + builder.addEdge(XcfaEdge(startLocation, edge.target, label, edge.metadata)) + } + } + + private fun count(transFunc: ExplStmtTransFunc): Int? { + if (!properlyUnrollable) return null + check(loopVar != null && loopVarModifiers != null && loopVarInit != null) + check(loopStartEdges.size == 1) + val prec = ExplPrec.of(listOf(loopVar)) var state = ExplState.of(ImmutableValuation.empty()) state = transFunc.getSuccStates(state, BasicStmtAction(loopVarInit), prec).first() var cnt = 0 - while (!transFunc.getSuccStates(state, BasicStmtAction(loopCondEdge), prec).first().isBottom) { + val loopCondAction = BasicStmtAction(loopStartEdges.first()) + while (!transFunc.getSuccStates(state, loopCondAction, prec).first().isBottom) { cnt++ - if (cnt > UNROLL_LIMIT) return -1 + if (UNROLL_LIMIT in 0 until cnt) return null state = transFunc.getSuccStates(state, BasicStmtAction(loopVarModifiers), prec).first() } return cnt } - private fun XcfaLabel.removeCondition(): XcfaLabel { - val stmtToRemove = getFlatLabels().find { - it is StmtLabel && it.stmt is AssumeStmt && (it.collectVars() - loopVar).isEmpty() - } - return when { - this == stmtToRemove -> NopLabel - - this is SequenceLabel -> SequenceLabel( - labels.map { it.removeCondition() }, metadata - ) - - else -> this - } - } - - private fun copyBody(builder: XcfaProcedureBuilder, startLocation: XcfaLocation, index: Int): XcfaLocation { + private fun copyBody(builder: XcfaProcedureBuilder, startLoc: XcfaLocation, index: Int, removeCond: Boolean) + : XcfaLocation { val locs = loopLocs.associateWith { - val loc = XcfaLocation("loop${index}_${it.name}") + val loc = XcfaLocation("${it.name}_loop${index}") builder.addLoc(loc) loc } loopEdges.forEach { - val newSource = if (it.source == loopStart) startLocation else checkNotNull(locs[it.source]) - val newLabel = if (it.source == loopStart) it.label.removeCondition() else it.label - val edge = XcfaEdge(newSource, checkNotNull(locs[it.target]!!), newLabel, it.metadata) + val newSource = if (it.source == loopStart) startLoc else locs[it.source]!! + val newLabel = if (it.source == loopStart && removeCond) it.label.removeCondition() else it.label + val edge = XcfaEdge(newSource, locs[it.target]!!, newLabel, it.metadata) builder.addEdge(edge) } - return checkNotNull(locs[loopStart]) - } - - fun unroll(builder: XcfaProcedureBuilder, transFunc: ExplStmtTransFunc) { - val count = count(transFunc) - if (count == -1) return + exitEdges.forEach { (loc, edges) -> + for (edge in edges) { + if (removeCond && loc == loopStart) continue + val source = if (loc == loopStart) startLoc else locs[loc]!! + builder.addEdge(XcfaEdge(source, edge.target, edge.label, edge.metadata)) + } + } - (loopLocs - loopStart).forEach(builder::removeLoc) - loopEdges.forEach(builder::removeEdge) - builder.removeEdge(exitCondEdge) + return locs[loopStart]!! + } - var startLocation = loopStart - for (i in 0 until count) { - startLocation = copyBody(builder, startLocation, i) + private fun XcfaLabel.removeCondition(): XcfaLabel { + val stmtToRemove = getFlatLabels().find { + it is StmtLabel && it.stmt is AssumeStmt && (it.collectVars() - loopVar).isEmpty() + } + return when { + this == stmtToRemove -> NopLabel + this is SequenceLabel -> SequenceLabel(labels.map { it.removeCondition() }, metadata) + else -> this } - - builder.addEdge( - XcfaEdge( - source = startLocation, - target = exitCondEdge.target, - label = exitCondEdge.label.removeCondition(), - metadata = exitCondEdge.metadata - ) - ) } } @@ -153,7 +169,7 @@ class LoopUnrollPass : ProcedurePass { } else { val edge = edgesToExplore.random() if (edge.target in stack) { // loop found - isUnrollable(edge.target, stack)?.let { return it } + getLoop(edge.target)?.let { return it } } else { stack.push(edge.target) } @@ -163,78 +179,130 @@ class LoopUnrollPass : ProcedurePass { return null } - private fun isUnrollable(loopStart: XcfaLocation, stack: Stack): Loop? { - if (loopStart.outgoingEdges.size != 2) return null - val loopLocations = stack.slice(stack.lastIndexOf(loopStart) until stack.size) - val loopEdges = loopLocations.flatMap { loc -> - if (loc == loopStart) { - val loopEdge = loc.outgoingEdges.filter { it.target in loopLocations } - if (loopEdge.size != 1) return null - loopEdge - } else { - if (!loopLocations.containsAll(loc.outgoingEdges.map { it.target })) { - return null - } - loc.outgoingEdges - } + /** + * Find a loop from the given start location that can be unrolled. + */ + private fun getLoop(loopStart: XcfaLocation): Loop? { + var properlyUnrollable = true + if (loopStart.outgoingEdges.size != 2) { + properlyUnrollable = false // more than two outgoing edges from the loop start not supported } + val (loopLocations, loopEdges) = getLoopElements(loopStart) + if (loopEdges.isEmpty()) return null // unsupported loop structure + + val loopCondEdges = loopStart.outgoingEdges.filter { it.target in loopLocations } + if (loopCondEdges.size != 1) properlyUnrollable = false // more than one loop condition not supported + + // find the loop variable based on the outgoing edges from the loop start location val loopVar = loopStart.outgoingEdges.map { val vars = it.label.collectVarsWithAccessType() - if (vars.size != 1) return null - vars.keys.first() - }.reduce { v1, v2 -> if (v1 != v2) return null else v1 } - - var edge = loopStart.outgoingEdges.find { it.target in loopLocations }!! - val necessaryLoopEdges = mutableSetOf(edge) - while (edge.target.outgoingEdges.size == 1) { - edge = edge.target.outgoingEdges.first() - necessaryLoopEdges.add(edge) - } - val finalEdges = loopStart.incomingEdges.filter { it.source in loopLocations } - if (finalEdges.size == 1) { - edge = finalEdges.first() - necessaryLoopEdges.add(edge) - while (edge.source.incomingEdges.size == 1) { - edge = edge.source.incomingEdges.first() + if (vars.size != 1) { + null // multiple variables in the loop condition not supported + } else { + vars.keys.first() + } + }.reduce { v1, v2 -> if (v1 != v2) null else v1 } + if (loopVar == null) properlyUnrollable = false + + val (loopVarInit, loopVarModifiers) = run { + if (!properlyUnrollable) return@run null + + // find (a subset of) edges that are executed in every loop iteration + var edge = loopStart.outgoingEdges.find { it.target in loopLocations }!! + val necessaryLoopEdges = mutableSetOf(edge) + while (edge.target.outgoingEdges.size == 1) { + edge = edge.target.outgoingEdges.first() necessaryLoopEdges.add(edge) } - } + val finalEdges = loopStart.incomingEdges.filter { it.source in loopLocations } + if (finalEdges.size == 1) { + edge = finalEdges.first() + necessaryLoopEdges.add(edge) + while (edge.source.incomingEdges.size == 1) { + edge = edge.source.incomingEdges.first() + necessaryLoopEdges.add(edge) + } + } - val loopVarModifiers = loopEdges.filter { - val vars = it.label.collectVarsWithAccessType() - if (vars[loopVar].isWritten) { - if (it !in necessaryLoopEdges || vars.size > 1) return null - true - } else { - false + // find edges that modify the loop variable + val loopVarModifiers = loopEdges.filter { + val vars = it.label.collectVarsWithAccessType() + if (vars[loopVar].isWritten) { + if (it !in necessaryLoopEdges || vars.size > 1) return@run null // loop variable modification cannot be determined statically + true + } else { + false + } } - } - lateinit var loopVarInit: XcfaEdge - var loc = loopStart - while (true) { - val inEdges = loc.incomingEdges.filter { it.source !in loopLocations } - if (inEdges.size != 1) return null - val inEdge = inEdges.first() - val vars = inEdge.label.collectVarsWithAccessType() - if (vars[loopVar].isWritten) { - if (vars.size > 1) return null - loopVarInit = inEdge - break - } - loc = inEdge.source + // find loop variable initialization before the loop + lateinit var loopVarInit: XcfaEdge + var loc = loopStart + while (true) { + val inEdges = loc.incomingEdges.filter { it.source !in loopLocations } + if (inEdges.size != 1) return@run null + val inEdge = inEdges.first() + val vars = inEdge.label.collectVarsWithAccessType() + if (vars[loopVar].isWritten) { + if (vars.size > 1) return@run null + loopVarInit = inEdge + break + } + loc = inEdge.source + } + + loopVarInit to loopVarModifiers + } ?: run { + properlyUnrollable = false + null to null } + val exits = loopLocations.mapNotNull { loc -> + val exitEdges = loc.outgoingEdges.filter { it.target !in loopLocations } + if (exitEdges.isEmpty()) null else (loc to exitEdges) + }.toMap() return Loop( - loopVar = loopVar, - loopVarModifiers = loopVarModifiers, - loopVarInit = loopVarInit, - loopCondEdge = loopStart.outgoingEdges.find { it.target in loopLocations }!!, - exitCondEdge = loopStart.outgoingEdges.find { it.target !in loopLocations }!!, loopStart = loopStart, loopLocs = loopLocations, - loopEdges = loopEdges + loopEdges = loopEdges, + loopVar = loopVar, + loopVarInit = loopVarInit, + loopVarModifiers = loopVarModifiers, + loopStartEdges = loopCondEdges, + exitEdges = exits, + properlyUnrollable = properlyUnrollable, ).also { if (it in testedLoops) return null } } + + /** + * Find loop locations and edges. + */ + private fun getLoopElements(loopStart: XcfaLocation): Pair, Set> { + val backSearch: (XcfaLocation) -> Pair, List>? = backSearch@{ startLoc -> + val locs = mutableSetOf() + val edges = mutableListOf() + val toVisit = mutableListOf(startLoc) + while (toVisit.isNotEmpty()) { + val current = toVisit.removeFirst() + if (current == loopStart) continue + if (current.incomingEdges.size == 0) return@backSearch null // not part of the loop + if (locs.add(current)) { + edges.addAll(current.incomingEdges) + toVisit.addAll(current.incomingEdges.map { it.source }) + } + } + locs to edges + } + + val locs = mutableSetOf(loopStart) + val edges = mutableSetOf() + loopStart.incomingEdges.forEach { incoming -> + val (l, e) = backSearch(incoming.source) ?: return@forEach + locs.addAll(l) + edges.addAll(e) + edges.add(incoming) + } + return locs to edges + } } \ No newline at end of file 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 new file mode 100644 index 0000000000..07a1b62bb9 --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MallocFunctionPass.kt @@ -0,0 +1,93 @@ +/* + * 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.passes + +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 +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.getFlatLabels +import hu.bme.mit.theta.xcfa.model.* +import org.abego.treelayout.internal.util.Contract.checkState + +/** + * Transforms mallocs into address assignments. + * Requires the ProcedureBuilder be `deterministic`. + */ +class MallocFunctionPass(val parseContext: ParseContext) : ProcedurePass { + + private val XcfaBuilder.malloc: VarDecl<*> by lazy { + Var("__malloc", CPointer(null, null, parseContext).smtType) + } + + 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 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 { + val initAssign = StmtLabel(Assign(cast(mallocVar, mallocVar.type), + cast(CComplexType.getType(ret, parseContext).nullValue, mallocVar.type))) + val newEdges = it.initLoc.outgoingEdges.map { + it.withLabel( + SequenceLabel(listOf(initAssign) + it.label.getFlatLabels(), it.label.metadata)) + } + it.initLoc.outgoingEdges.forEach(it::removeEdge) + newEdges.forEach(it::addEdge) + } + } + val assign1 = AssignStmt.of( + cast(mallocVar, ret.type), + cast(Add(mallocVar.ref, CComplexType.getType(ret, parseContext).getValue("3")), + ret.type)) + val assign2 = AssignStmt.of( + cast(ret.decl as VarDecl<*>, ret.type), cast(mallocVar.ref, ret.type)) + builder.addEdge(XcfaEdge(it.source, it.target, SequenceLabel( + listOf( + StmtLabel(assign1, metadata = invokeLabel.metadata), + StmtLabel(assign2, metadata = invokeLabel.metadata))))) + } else { + builder.addEdge(it) + } + } + } + } + return builder + } + + private fun predicate(it: XcfaLabel): Boolean { + return it is InvokeLabel && it.name == "malloc" + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..f61bc4e10e --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/MutexToVarPass.kt @@ -0,0 +1,115 @@ +/* + * 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.passes + +import hu.bme.mit.theta.core.decl.Decls +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.type.booltype.BoolExprs.* +import hu.bme.mit.theta.core.type.booltype.BoolType +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; + */ +class MutexToVarPass : ProcedurePass { + + 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 + } + } + + 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)) + } + } + } + return builder + } + + 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 + } + } + + private val String.mutexFlag get() = flag("_mutex_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 diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NoSideEffectPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NoSideEffectPass.kt new file mode 100644 index 0000000000..ffcda3e8bb --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NoSideEffectPass.kt @@ -0,0 +1,54 @@ +/* + * 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.passes + +import hu.bme.mit.theta.frontend.ParseContext +import hu.bme.mit.theta.xcfa.model.* + +/** + * Transforms all ignored calls into nop/skip labels. + * Requires the ProcedureBuilder be `deterministic`. + */ +class NoSideEffectPass(val parseContext: ParseContext) : ProcedurePass { + + 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])) { + builder.addEdge(XcfaEdge(it.source, it.target, SequenceLabel(listOf(NopLabel)))) + } else { + builder.addEdge(it) + } + } + } + } + return builder + } + + private fun predicate(label: XcfaLabel): Boolean { + return label is InvokeLabel && listOf( + Regex("sleep"), + Regex("free"), + Regex("pthread_mutex_destroy"), // TODO: is this safe? + ).any { label.name.matches(it) } + } +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NondetFunctionPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NondetFunctionPass.kt index 6d32ffb75b..fd7dfcb847 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NondetFunctionPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/NondetFunctionPass.kt @@ -19,14 +19,13 @@ package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.HavocStmt import hu.bme.mit.theta.core.type.anytype.RefExpr -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.* /** * Transforms all procedure calls into havocs. * Requires the ProcedureBuilder be `deterministic`. */ -class NondetFunctionPass(val parseContext: ParseContext) : ProcedurePass { +class NondetFunctionPass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) 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 5bbfc3001e..418090364d 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 @@ -18,7 +18,6 @@ package hu.bme.mit.theta.xcfa.passes import hu.bme.mit.theta.core.stmt.AssumeStmt import hu.bme.mit.theta.core.type.booltype.BoolExprs.True -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.* /** @@ -27,7 +26,7 @@ import hu.bme.mit.theta.xcfa.model.* * Sets the `normal` flag on the ProcedureBuilder */ -class NormalizePass(val parseContext: ParseContext) : ProcedurePass { +class NormalizePass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { val edges = LinkedHashSet(builder.getEdges()) 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 dfd581d8b5..02cb00683e 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 @@ -27,69 +27,77 @@ open class ProcedurePassManager(vararg passes: List) { class CPasses(checkOverflow: Boolean, parseContext: ParseContext, uniqueWarningLogger: Logger) : ProcedurePassManager( listOf( // formatting - NormalizePass(parseContext), - DeterministicPass(parseContext), + NormalizePass(), + DeterministicPass(), // removing redundant elements - EmptyEdgeRemovalPass(parseContext), - UnusedLocRemovalPass(parseContext), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), // handling intrinsics - ErrorLocationPass(checkOverflow, parseContext), - FinalLocationPass(checkOverflow, parseContext), - SvCompIntrinsicsPass(parseContext), + ErrorLocationPass(checkOverflow), + FinalLocationPass(checkOverflow), + SvCompIntrinsicsPass(), FpFunctionsToExprsPass(parseContext), - CLibraryFunctionsPass(parseContext), + CLibraryFunctionsPass(), + ), + listOf( + // trying to inline procedures + InlineProceduresPass(parseContext), + RemoveDeadEnds(), + EliminateSelfLoops(), + ), + listOf( + ReferenceElimination(parseContext), + MallocFunctionPass(parseContext), ), listOf( // optimizing SimplifyExprsPass(parseContext), LoopUnrollPass(), + SimplifyExprsPass(parseContext), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), ), listOf( - // trying to inline procedures - InlineProceduresPass(parseContext), - RemoveDeadEnds(parseContext), - EliminateSelfLoops(parseContext), + StaticCoiPass(), ), listOf( // handling remaining function calls - NondetFunctionPass(parseContext), + NoSideEffectPass(parseContext), + NondetFunctionPass(), LbePass(parseContext), - NormalizePass(parseContext), // needed after lbe, TODO - DeterministicPass(parseContext), // needed after lbe, TODO + NormalizePass(), // needed after lbe, TODO + DeterministicPass(), // needed after lbe, TODO HavocPromotionAndRange(parseContext), // Final cleanup - UnusedVarPass(parseContext, uniqueWarningLogger), +// UnusedVarPass(uniqueWarningLogger), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), + ), + listOf( + FetchExecuteWriteback(parseContext) ) ) class ChcPasses(parseContext: ParseContext, uniqueWarningLogger: Logger) : ProcedurePassManager(listOf( // formatting - NormalizePass(parseContext), - DeterministicPass(parseContext), + NormalizePass(), + DeterministicPass(), // removing redundant elements - EmptyEdgeRemovalPass(parseContext), - UnusedLocRemovalPass(parseContext), + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass(), // optimizing SimplifyExprsPass(parseContext), - // handling intrinsics -// ErrorLocationPass(false), -// FinalLocationPass(false), -// SvCompIntrinsicsPass(), -// FpFunctionsToExprsPass(), -// PthreadFunctionsPass(), - // trying to inline procedures ), listOf( + // trying to inline procedures InlineProceduresPass(parseContext), - RemoveDeadEnds(parseContext), - EliminateSelfLoops(parseContext), + RemoveDeadEnds(), + EliminateSelfLoops(), // handling remaining function calls -// NondetFunctionPass(), LbePass(parseContext), - NormalizePass(parseContext), // needed after lbe, TODO - DeterministicPass(parseContext), // needed after lbe, TODO -// HavocPromotionAndRange(), + NormalizePass(), // needed after lbe, TODO + DeterministicPass(), // needed after lbe, TODO // Final cleanup - UnusedVarPass(parseContext, uniqueWarningLogger), + UnusedVarPass(uniqueWarningLogger), )) class LitmusPasses : ProcedurePassManager() \ No newline at end of file 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 new file mode 100644 index 0000000000..e06e7ce39f --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/ReferenceElimination.kt @@ -0,0 +1,194 @@ +/* + * 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.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.* +import hu.bme.mit.theta.core.type.Expr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.Dereference +import hu.bme.mit.theta.core.type.anytype.Exprs.Dereference +import hu.bme.mit.theta.core.type.anytype.RefExpr +import hu.bme.mit.theta.core.type.anytype.Reference +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.getFlatLabels +import hu.bme.mit.theta.xcfa.model.* +import hu.bme.mit.theta.xcfa.references + +/** + * Removes all references in favor of creating arrays instead. + */ + +class ReferenceElimination(val parseContext: ParseContext) : ProcedurePass { + + companion object { + + private var cnt = 2 // counts upwards, uses 3k+2 + get() = field.also { field += 3 } + } + + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + val globalReferredVars = builder.parent.metaData.computeIfAbsent("references") { + builder.parent.getProcedures().flatMap { + it.getEdges() + .flatMap { it.label.getFlatLabels().flatMap { it.references } } + } + .map { (it.expr as RefExpr<*>).decl as VarDecl<*> }.toSet() + .filter { builder.parent.getVars().any { global -> global.wrappedVar == it } } + .associateWith { + val ptrType = CPointer(null, CComplexType.getType(it.ref, parseContext), parseContext) + val varDecl = Var(it.name + "*", ptrType.smtType) + 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))) + Pair(varDecl, assign) + } + } + checkState(globalReferredVars is Map<*, *>, "ReferenceElimination needs info on references") + globalReferredVars as Map, Pair, StmtLabel>> + + val referredVars = builder.getEdges() + .flatMap { it.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) + val varDecl = Var(it.name + "*", ptrType.smtType) + builder.addVar(varDecl) + parseContext.metadata.create(varDecl.ref, "cType", ptrType) + val assign = StmtLabel(AssignStmt.of(cast(varDecl, varDecl.type), + cast(CComplexType.getType(varDecl.ref, parseContext).getValue("$cnt"), varDecl.type))) + Pair(varDecl, assign) + } + globalReferredVars + + if (referredVars.isEmpty()) { + return builder + } + + if (builder.parent.getInitProcedures().any { it.first == builder }) { // we only need this for main + val initLabels = referredVars.values.map { it.second } + 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) + } + + val edges = LinkedHashSet(builder.getEdges()) + for (edge in edges) { + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(edge.label.changeReferredVars(referredVars, parseContext))) + } + + return DeterministicPass().run(NormalizePass().run(builder)) + } + + @JvmOverloads + fun XcfaLabel.changeReferredVars(varLut: Map, Pair, StmtLabel>>, + parseContext: ParseContext? = null): XcfaLabel = + if (varLut.isNotEmpty()) + when (this) { + is InvokeLabel -> InvokeLabel(name, params.map { it.changeReferredVars(varLut, parseContext) }, + metadata = metadata) + + is NondetLabel -> NondetLabel(labels.map { it.changeReferredVars(varLut, parseContext) }.toSet(), + metadata = metadata) + + is SequenceLabel -> SequenceLabel(labels.map { it.changeReferredVars(varLut, parseContext) }, + metadata = metadata) + + is StartLabel -> StartLabel(name, params.map { it.changeReferredVars(varLut, parseContext) }, + pidVar, metadata = metadata) + + is StmtLabel -> SequenceLabel(stmt.changeReferredVars(varLut, parseContext).map { + StmtLabel(it, metadata = metadata, + choiceType = this.choiceType) + }).let { if (it.labels.size == 1) it.labels[0] else it } + + else -> this + } + else this + + @JvmOverloads + fun Stmt.changeReferredVars(varLut: Map, Pair, StmtLabel>>, + parseContext: ParseContext? = null): List { + val stmts = when (this) { + is AssignStmt<*> -> if (this.varDecl in varLut.keys) { + val newVar = varLut[this.varDecl]!!.first + listOf( + MemoryAssignStmt.create( + Dereference( + cast(newVar.ref, newVar.type), + cast(CComplexType.getSignedLong(parseContext).nullValue, newVar.type), + this.expr.type), + this.expr.changeReferredVars(varLut, parseContext))) + } else { + listOf(AssignStmt.of(cast(this.varDecl, this.varDecl.type), + cast(this.expr.changeReferredVars(varLut, parseContext), this.varDecl.type))) + } + + is MemoryAssignStmt<*, *, *> -> listOf( + MemoryAssignStmt.create(deref.changeReferredVars(varLut, parseContext) as Dereference<*, *, *>, + expr.changeReferredVars(varLut, parseContext))) + + is AssumeStmt -> listOf(AssumeStmt.of(cond.changeReferredVars(varLut, parseContext))) + is SequenceStmt -> listOf( + SequenceStmt.of(this.stmts.flatMap { it.changeReferredVars(varLut, parseContext) })) + + is SkipStmt -> listOf(this) + else -> TODO("Not yet implemented ($this)") + } + val metadataValue = parseContext?.metadata?.getMetadataValue(this, "sourceStatement") + if (metadataValue?.isPresent == true) { + for (stmt in stmts) { + parseContext.metadata.create(stmt, "sourceStatement", metadataValue.get()) + } + } + return stmts + } + + @JvmOverloads + fun Expr.changeReferredVars(varLut: Map, Pair, StmtLabel>>, + parseContext: ParseContext? = null): Expr = + if (this is RefExpr) { + (decl as VarDecl).changeReferredVars(varLut) + } else if (this is Reference<*, *> && this.expr is RefExpr<*> && (this.expr as RefExpr<*>).decl in varLut.keys) { + varLut[(this.expr as RefExpr<*>).decl]?.first?.ref as Expr + } else { + val ret = this.withOps(this.ops.map { it.changeReferredVars(varLut, parseContext) }) + if (parseContext?.metadata?.getMetadataValue(this, "cType")?.isPresent == true) { + parseContext.metadata?.create(ret, "cType", CComplexType.getType(this, parseContext)) + } + ret + } + + fun VarDecl.changeReferredVars( + varLut: Map, Pair, StmtLabel>>): Expr = + varLut[this]?.first?.let { + Dereference(cast(it.ref, it.type), cast(CComplexType.getSignedInt(parseContext).nullValue, it.type), + this.type) as Expr + } ?: this.ref + +} \ No newline at end of file diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/RemoveDeadEnds.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/RemoveDeadEnds.kt index 3d25e5ce26..6f575f6c24 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/RemoveDeadEnds.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/RemoveDeadEnds.kt @@ -15,12 +15,11 @@ */ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.getFlatLabels import hu.bme.mit.theta.xcfa.model.* import java.util.stream.Collectors -class RemoveDeadEnds(val parseContext: ParseContext) : ProcedurePass { +class RemoveDeadEnds : ProcedurePass { // TODO: thread start and procedure call should not be dead-end! Use-case: while(1) pthread_create(..); override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { @@ -53,7 +52,7 @@ class RemoveDeadEnds(val parseContext: ParseContext) : ProcedurePass { private fun filterReachableEdges(loc: XcfaLocation, reachableEdges: MutableSet) { val outgoingEdges: MutableSet = LinkedHashSet(loc.outgoingEdges) - while (!outgoingEdges.isEmpty()) { + while (outgoingEdges.isNotEmpty()) { val any = outgoingEdges.stream().findAny() val outgoingEdge = any.get() outgoingEdges.remove(outgoingEdge) @@ -66,7 +65,7 @@ class RemoveDeadEnds(val parseContext: ParseContext) : ProcedurePass { private fun collectNonDeadEndEdges(loc: XcfaLocation, nonDeadEndEdges: MutableSet) { val incomingEdges: MutableSet = LinkedHashSet(loc.incomingEdges) - while (!incomingEdges.isEmpty()) { + while (incomingEdges.isNotEmpty()) { val any = incomingEdges.stream().findAny() val incomingEdge = any.get() incomingEdges.remove(incomingEdge) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SimplifyExprsPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SimplifyExprsPass.kt index b1cbcdef1f..84d3648685 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SimplifyExprsPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SimplifyExprsPass.kt @@ -16,26 +16,20 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.model.ImmutableValuation import hu.bme.mit.theta.core.model.MutableValuation 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.Stmts.Assign import hu.bme.mit.theta.core.stmt.Stmts.Assume -import hu.bme.mit.theta.core.type.Expr -import hu.bme.mit.theta.core.type.Type -import hu.bme.mit.theta.core.type.abstracttype.NeqExpr -import hu.bme.mit.theta.core.utils.ExprUtils -import hu.bme.mit.theta.core.utils.ExprUtils.simplify -import hu.bme.mit.theta.core.utils.StmtUtils -import hu.bme.mit.theta.core.utils.TypeUtils.cast +import hu.bme.mit.theta.core.type.booltype.BoolExprs.False import hu.bme.mit.theta.frontend.ParseContext -import hu.bme.mit.theta.frontend.transformation.model.types.complex.CComplexType import hu.bme.mit.theta.xcfa.collectVarsWithAccessType -import hu.bme.mit.theta.xcfa.isRead -import hu.bme.mit.theta.xcfa.model.* +import hu.bme.mit.theta.xcfa.getFlatLabels +import hu.bme.mit.theta.xcfa.isWritten +import hu.bme.mit.theta.xcfa.model.SequenceLabel +import hu.bme.mit.theta.xcfa.model.StmtLabel +import hu.bme.mit.theta.xcfa.model.XcfaEdge +import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder +import hu.bme.mit.theta.xcfa.simplify /** * This pass simplifies the expressions inside statements and substitutes the values of constant variables @@ -48,118 +42,75 @@ class SimplifyExprsPass(val parseContext: ParseContext) : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) - removeUnusedGlobalVarWrites(builder) - val valuation = findConstVariables(builder) - val edges = LinkedHashSet(builder.getEdges()) - for (edge in edges) { - val newLabels = (edge.label as SequenceLabel).labels.map { - if (it is StmtLabel) when (it.stmt) { - is AssignStmt<*> -> { - val simplified = simplify(it.stmt.expr, valuation) - if (parseContext.metadata.getMetadataValue(it.stmt.expr, "cType").isPresent) - parseContext.metadata.create(simplified, "cType", - CComplexType.getType(it.stmt.expr, parseContext)) - StmtLabel(Assign(cast(it.stmt.varDecl, it.stmt.varDecl.type), - cast(simplified, it.stmt.varDecl.type)), metadata = it.metadata) + val unusedLocRemovalPass = UnusedLocRemovalPass() + val valuations = LinkedHashMap() + var edges = LinkedHashSet(builder.getEdges()) + val constValuation = MutableValuation() + val modifiedGlobalVars = builder.parent.getVars().map { it.wrappedVar }.filter { v -> + var firstWrite: XcfaEdge? = null + (builder.parent.getProcedures().sumOf { p -> + p.getEdges().count { e -> + e.getFlatLabels().any { l -> + l.collectVarsWithAccessType().any { it.value.isWritten && it.key == v } + }.also { written -> + if (written && firstWrite == null) firstWrite = e } - - is AssumeStmt -> { - val simplified = simplify(it.stmt.cond, valuation) - if (parseContext.metadata.getMetadataValue(it.stmt.cond, "cType").isPresent) { - parseContext.metadata.create(simplified, "cType", - CComplexType.getType(it.stmt.cond, parseContext)) - } - parseContext.metadata.create(simplified, "cTruth", it.stmt.cond is NeqExpr<*>) - StmtLabel(Assume(simplified), metadata = it.metadata, choiceType = it.choiceType) - } - - else -> it - } else it - } - if (newLabels != edge.label.labels) { - builder.removeEdge(edge) - builder.addEdge(edge.withLabel(SequenceLabel(newLabels))) + } + } > 1).also { modified -> + if (!modified && firstWrite != null) { + val valuation = MutableValuation() + firstWrite!!.getFlatLabels().forEach { it.simplify(valuation, parseContext) } + valuation.toMap()[v]?.let { constValuation.put(v, it) } + } } } - builder.metaData["simplifiedExprs"] = Unit - return builder - } + lateinit var lastEdges: LinkedHashSet + do { + lastEdges = edges - private fun removeUnusedGlobalVarWrites(builder: XcfaProcedureBuilder) { - val usedVars = mutableSetOf>() - val xcfaBuilder = builder.parent - xcfaBuilder.getProcedures().flatMap { it.getEdges() }.forEach { - it.label.collectVarsWithAccessType().forEach { (varDecl, access) -> - if (access.isRead) usedVars.add(varDecl) - } - } - val unusedVars = xcfaBuilder.getVars().map { it.wrappedVar } union builder.getVars() subtract - usedVars subtract builder.getParams().map { it.first }.toSet() - xcfaBuilder.getProcedures().forEach { b -> - b.getEdges().toList().forEach { edge -> - val newLabel = edge.label.removeUnusedWrites(unusedVars) - b.removeEdge(edge) - b.addEdge(edge.withLabel(newLabel)) - } - } - } + val toVisit = builder.initLoc.outgoingEdges.toMutableList() + val visited = mutableSetOf() + while (toVisit.isNotEmpty()) { + val edge = toVisit.removeFirst() + visited.add(edge) + + val incomingValuations = edge.source.incomingEdges + .filter { it.getFlatLabels().none { l -> l is StmtLabel && l.stmt == Assume(False()) } } + .map(valuations::get).reduceOrNull(this::intersect) + val localValuation = MutableValuation.copyOf(incomingValuations ?: ImmutableValuation.empty()) + localValuation.putAll(constValuation) + val oldLabels = edge.getFlatLabels() + val newLabels = oldLabels.map { it.simplify(localValuation, parseContext) } - private fun findConstVariables(builder: XcfaProcedureBuilder): Valuation { - val valuation = MutableValuation() - builder.parent.getProcedures() - .flatMap { it.getEdges() } - .map { it.label.collectVarsWithLabels() } - .filter { it.isNotEmpty() }.merge() - .map { - val writes = it.value.filter { label -> label.isWrite(it.key) } - if (writes.size == 1 && writes.first() is StmtLabel) { - val label = writes.first() as StmtLabel - if (label.stmt is AssignStmt<*> && label.stmt.expr.isConst()) { - return@map label.stmt + // note that global variable values are still propagated within an edge (XcfaEdge is considered atomic) + modifiedGlobalVars.forEach { localValuation.remove(it) } + + if (newLabels != oldLabels) { + builder.removeEdge(edge) + valuations.remove(edge) + if (newLabels.firstOrNull().let { (it as? StmtLabel)?.stmt != Assume(False()) }) { + val newEdge = edge.withLabel(SequenceLabel(newLabels)) + builder.addEdge(newEdge) + valuations[newEdge] = localValuation } + } else { + valuations[edge] = localValuation } - null - } - .filterNotNull() - .forEach { assignment -> - try { - valuation.put(assignment.varDecl, assignment.expr.eval(ImmutableValuation.empty())) - } catch (_: UnsupportedOperationException) { - } - } - return valuation - } - private fun List, List>>.merge(): Map, List> = - this.fold(mapOf()) { acc, next -> - (acc.keys + next.keys).associateWith { - mutableListOf().apply { - acc[it]?.let { addAll(it) } - next[it]?.let { addAll(it) } - } + toVisit.addAll(edge.target.outgoingEdges.filter { it !in visited }) } - } - - private fun XcfaLabel.collectVarsWithLabels(): Map, List> = when (this) { - is StmtLabel -> StmtUtils.getVars(stmt).associateWith { listOf(this) } - is NondetLabel -> labels.map { it.collectVarsWithLabels() }.merge() - is SequenceLabel -> labels.map { it.collectVarsWithLabels() }.merge() - is InvokeLabel -> params.map { ExprUtils.getVars(it) }.flatten().associateWith { listOf(this) } - is JoinLabel -> mapOf(pidVar to listOf(this)) - is ReadLabel -> mapOf(global to listOf(this), local to listOf(this)) - is StartLabel -> params.map { ExprUtils.getVars(it) }.flatten() - .associateWith { listOf(this) } + mapOf(pidVar to listOf(this)) + unusedLocRemovalPass.run(builder) - is WriteLabel -> mapOf(global to listOf(this), local to listOf(this)) - else -> emptyMap() + edges = LinkedHashSet(builder.getEdges()) + } while (lastEdges != edges) + builder.metaData["simplifiedExprs"] = Unit + return builder } - private fun XcfaLabel.isWrite(v: VarDecl<*>) = - this is StmtLabel && this.stmt is AssignStmt<*> && this.stmt.varDecl == v - - private fun Expr.isConst(): Boolean { - val vars = mutableListOf>() - ExprUtils.collectVars(this, vars) - return vars.isEmpty() + private fun intersect(v1: Valuation?, v2: Valuation?): Valuation { + if (v1 == null || v2 == null) return ImmutableValuation.empty() + val v1map = v1.toMap() + val v2map = v2.toMap() + return ImmutableValuation.from(v1map.filter { v2map.containsKey(it.key) && v2map[it.key] == it.value }) } } diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SsaPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SsaPass.kt new file mode 100644 index 0000000000..9dd3558796 --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/SsaPass.kt @@ -0,0 +1,133 @@ +/* + * 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.passes + +import hu.bme.mit.theta.core.decl.IndexedVarDecl +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.type.Expr +import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.RefExpr +import hu.bme.mit.theta.core.utils.ExprUtils +import hu.bme.mit.theta.core.utils.indexings.VarIndexing +import hu.bme.mit.theta.core.utils.indexings.VarIndexingFactory +import hu.bme.mit.theta.xcfa.model.* + +/** + * Transform the procedure to Static Single Assignment (SSA) form. + */ +class SSAPass : ProcedurePass { + + private val ssaUtils = SSAUtils() + + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + builder.getEdges().toSet().forEach { edge -> + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(ssaUtils.toSSA(edge))) + } + return builder + } +} + +internal class SSAUtils { + + private var indexing: VarIndexing = VarIndexingFactory.indexing(0) + + private val indexedVars = mutableSetOf>() + + fun toSSA(edge: XcfaEdge) = edge.label.toSSA() + fun removeSSA(edge: XcfaEdge) = edge.withLabel(edge.label.removeSSA()) + + // Convert to SSA + + private fun XcfaLabel.toSSA(): XcfaLabel { + return when (this) { + is StmtLabel -> { + when (val stmt = this.stmt) { + is AssignStmt<*> -> StmtLabel(stmt.toSSA(), choiceType, metadata) + is AssumeStmt -> StmtLabel(stmt.toSSA(), choiceType, metadata) + is HavocStmt<*> -> StmtLabel(stmt.toSSA(), choiceType, metadata) + else -> error("Unsupported statement at SSA conversion: $stmt") + } + } + + is StartLabel -> StartLabel(name, params.map { it.toSSA() }, pidVar.getIndexed(), metadata, tempLookup) + is JoinLabel -> JoinLabel(pidVar.getIndexed(), metadata) + is SequenceLabel -> SequenceLabel(labels.map { it.toSSA() }, metadata) + is NopLabel, is FenceLabel -> this + else -> error("Unsupported label at SSA conversion: $this") + } + } + + private fun Expr.toSSA(): Expr { + val unfolded = toSSAAtomic() + ExprUtils.getVars(this).forEach { indexing = indexing.inc(it) } + return unfolded + } + + private fun Expr.toSSAAtomic(): Expr = if (this is RefExpr) { + (decl as? VarDecl)?.getIndexed(false)?.ref ?: this + } else { + map { it.toSSAAtomic() } + } + + private fun VarDecl.getIndexed(increment: Boolean = true): VarDecl { + val newName = this.name + "#" + indexing[this] + indexedVars.find { it.name == newName }?.let { return it as VarDecl } + val newVar = IndexedVarDecl.of(newName, this) + indexedVars.add(newVar) + if (increment) indexing = indexing.inc(this) + return newVar + } + + private fun AssignStmt.toSSA() = AssignStmt.of(varDecl.getIndexed(), expr.toSSA()) + private fun AssumeStmt.toSSA() = AssumeStmt.of(cond.toSSA()) + private fun HavocStmt.toSSA() = HavocStmt.of(varDecl.getIndexed()) + + // Remove SSA + + private fun XcfaLabel.removeSSA(): XcfaLabel { + return when (this) { + is StmtLabel -> { + when (val stmt = this.stmt) { + is AssignStmt<*> -> StmtLabel(stmt.removeSSA(), choiceType, metadata) + is AssumeStmt -> StmtLabel(stmt.removeSSA(), choiceType, metadata) + is HavocStmt<*> -> StmtLabel(stmt.removeSSA(), choiceType, metadata) + else -> this + } + } + + is StartLabel -> StartLabel(name, params.map { it.removeSSA() }, pidVar.noindex, metadata, tempLookup) + is JoinLabel -> JoinLabel(pidVar.noindex, metadata) + is SequenceLabel -> SequenceLabel(labels.map { it.removeSSA() }, metadata) + else -> this + } + } + + private fun Expr.removeSSA(): Expr = if (this is RefExpr) { + ((decl as? IndexedVarDecl)?.original ?: decl).ref + } else { + map { it.removeSSA() } + } + + private val VarDecl.noindex get() = (this as? IndexedVarDecl)?.original ?: this + private fun AssignStmt.removeSSA() = AssignStmt.of(varDecl.noindex, expr.removeSSA()) + private fun AssumeStmt.removeSSA() = AssumeStmt.of(cond.removeSSA()) + private fun HavocStmt.removeSSA() = HavocStmt.of(varDecl.noindex) +} diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/StaticCoiPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/StaticCoiPass.kt new file mode 100644 index 0000000000..c86b1e47ee --- /dev/null +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/StaticCoiPass.kt @@ -0,0 +1,124 @@ +/* + * 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.passes + +import hu.bme.mit.theta.core.decl.VarDecl +import hu.bme.mit.theta.core.stmt.AssignStmt +import hu.bme.mit.theta.core.stmt.HavocStmt +import hu.bme.mit.theta.xcfa.* +import hu.bme.mit.theta.xcfa.model.* + +class StaticCoiPass : ProcedurePass { + + companion object { + + var enabled = false + } + + private val directObservers: MutableMap> = mutableMapOf() + private val interProcessObservers: MutableMap> = mutableMapOf() + private val interProcessVarObservers: MutableMap, Set> = mutableMapOf() + + override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { + if (!enabled) return builder + + builder.parent.getProcedures().forEach { procedure -> + procedure.getEdges().forEach { edge -> + val flatLabels = edge.getFlatLabels() + flatLabels.forEachIndexed { index, label -> + if (label is StmtLabel) { + findDirectObservers(edge, label, flatLabels.subList(index + 1, flatLabels.size)) + findIndirectObservers(label, builder) + } + } + } + } + + builder.getEdges().toSet().forEach { edge -> + val labels = edge.getFlatLabels() + val kept = mutableListOf() + labels.forEach { label -> + if (!label.canBeSimplified || isObserved(label)) { + kept.add(label) + } + } + if (kept.size != labels.size) { + builder.removeEdge(edge) + builder.addEdge(edge.withLabel(SequenceLabel(kept, edge.label.metadata))) + } + } + + return builder + } + + private val XcfaLabel.canBeSimplified + get() = this is StmtLabel && + ((this.stmt is AssignStmt<*> && "_ret" !in this.stmt.varDecl.name) || this.stmt is HavocStmt<*>) && + dereferencesWithAccessTypes.none { it.second.isWritten } + + private fun findDirectObservers(edge: XcfaEdge, label: XcfaLabel, remaining: List) { + val writtenVars = label.collectVarsWithAccessType().filter { it.value.isWritten }.map { it.key }.toSet() + if (writtenVars.isEmpty()) return + + val toVisit = mutableListOf(edge) + val visited = mutableSetOf() + while (toVisit.isNotEmpty()) { + val visiting = toVisit.removeFirst() + visited.add(visiting) + val labels = if (visiting == edge) remaining else visiting.getFlatLabels() + labels.forEach { target -> + val vars = target.collectVarsWithAccessType() + if (vars.any { it.key in writtenVars && it.value.isRead }) { + directObservers[label] = directObservers.getOrDefault(label, setOf()) + target + } + } + + toVisit.addAll(visiting.target.outgoingEdges.filter { it !in visited }) + } + } + + private fun findIndirectObservers(label: XcfaLabel, builder: XcfaProcedureBuilder) { + val writtenVars = label.collectVarsWithAccessType().filter { it.value.isWritten }.map { it.key }.toSet() + if (writtenVars.isEmpty()) return + + interProcessObservers[label] = writtenVars.flatMap { v -> + interProcessVarObservers.getOrPut(v) { + builder.parent.getProcedures().flatMap { procedure -> + procedure.getEdges().flatMap { e -> + e.getFlatLabels().filter { l -> + l.collectVarsWithAccessType().any { it.key == v && it.value.isRead } + } + } + }.toSet() + } + }.toSet() + } + + private fun isObserved(label: XcfaLabel): Boolean { + val toVisit = mutableListOf(label) + val visited = mutableSetOf() + while (toVisit.isNotEmpty()) { + val visiting = toVisit.removeFirst() + if (visiting.collectAssumesVars().isNotEmpty()) return true + if (visiting.dereferencesWithAccessTypes.any { it.second.isWritten }) return true + + visited.add(visiting) + val toAdd = (directObservers[visiting] ?: emptySet()) union (interProcessObservers[visiting] ?: emptySet()) + toVisit.addAll(toAdd.filter { it !in visited }) + } + return false + } +} \ No newline at end of file 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 8fc0e6a416..46933ea4e9 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 @@ -16,7 +16,6 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.* import kotlin.jvm.optionals.getOrNull @@ -28,7 +27,7 @@ import kotlin.jvm.optionals.getOrNull * Requires the ProcedureBuilder be `deterministic`. */ @OptIn(ExperimentalStdlibApi::class) -class SvCompIntrinsicsPass(val parseContext: ParseContext) : ProcedurePass { +class SvCompIntrinsicsPass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) diff --git a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedLocRemovalPass.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedLocRemovalPass.kt index 6b6d1b1889..02211601fa 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedLocRemovalPass.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/UnusedLocRemovalPass.kt @@ -16,14 +16,13 @@ package hu.bme.mit.theta.xcfa.passes -import hu.bme.mit.theta.frontend.ParseContext import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder /** * Removes unused locations */ -class UnusedLocRemovalPass(val parseContext: ParseContext) : ProcedurePass { +class UnusedLocRemovalPass : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { builder.removeLocs { 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 0f4e0a7b58..1f88251fab 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 @@ -19,40 +19,74 @@ package hu.bme.mit.theta.xcfa.passes import com.google.common.collect.Sets import hu.bme.mit.theta.common.logging.Logger import hu.bme.mit.theta.core.decl.VarDecl -import hu.bme.mit.theta.frontend.ParseContext -import hu.bme.mit.theta.xcfa.collectVars -import hu.bme.mit.theta.xcfa.model.XcfaProcedureBuilder +import hu.bme.mit.theta.core.stmt.AssignStmt +import hu.bme.mit.theta.core.stmt.HavocStmt +import hu.bme.mit.theta.xcfa.collectVarsWithAccessType +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) */ -class UnusedVarPass(val parseContext: ParseContext, val uniqueWarningLogger: Logger) : ProcedurePass { +class UnusedVarPass(private val uniqueWarningLogger: Logger) : ProcedurePass { override fun run(builder: XcfaProcedureBuilder): XcfaProcedureBuilder { checkNotNull(builder.metaData["deterministic"]) val usedVars = LinkedHashSet>() - builder.getEdges().forEach { usedVars.addAll(it.label.collectVars()) } - val allVars = Sets.union(builder.getVars(), - builder.parent.getVars().map { it.wrappedVar }.toSet()) + 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 { - !varsAndParams.contains(it) - } - }\n") + "${usedVars.filter { it !in varsAndParams }}\n") } - val list = builder.getVars().filter { !usedVars.contains(it) }.toList() - list.forEach { - builder.removeVar(it) - } + builder.getVars().filter { it !in usedVars }.forEach { builder.removeVar(it) } return builder } + + private fun XcfaLabel.removeUnusedWrites(usedVars: Set>): XcfaLabel { + return when (this) { + is SequenceLabel -> + SequenceLabel(labels.map { it.removeUnusedWrites(usedVars) }.filter { it !is NopLabel }) + + 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/main/java/hu/bme/mit/theta/xcfa/passes/Utils.kt b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/Utils.kt index d9dfcef28e..cb5c74b8cb 100644 --- a/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/Utils.kt +++ b/subprojects/xcfa/xcfa/src/main/java/hu/bme/mit/theta/xcfa/passes/Utils.kt @@ -21,6 +21,7 @@ import hu.bme.mit.theta.core.decl.VarDecl import hu.bme.mit.theta.core.stmt.* import hu.bme.mit.theta.core.type.Expr import hu.bme.mit.theta.core.type.Type +import hu.bme.mit.theta.core.type.anytype.Dereference import hu.bme.mit.theta.core.type.anytype.RefExpr import hu.bme.mit.theta.core.utils.TypeUtils.cast import hu.bme.mit.theta.frontend.ParseContext @@ -109,6 +110,9 @@ fun Stmt.changeVars(varLut: Map, VarDecl<*>>, parseContext: ParseCon is AssignStmt<*> -> AssignStmt.of(cast(varDecl.changeVars(varLut), varDecl.type), cast(expr.changeVars(varLut, parseContext), varDecl.type)) + is MemoryAssignStmt<*, *, *> -> MemoryAssignStmt.create(deref.changeVars(varLut) as Dereference, + expr.changeVars(varLut)) + is HavocStmt<*> -> HavocStmt.of(varDecl.changeVars(varLut)) is AssumeStmt -> AssumeStmt.of(cond.changeVars(varLut, parseContext)) is SequenceStmt -> SequenceStmt.of(stmts.map { it.changeVars(varLut, parseContext) }) @@ -132,9 +136,13 @@ fun Expr.changeVars(varLut: Map, VarDecl<*>>, parseCon ret } -fun Decl.changeVars(varLut: Map, VarDecl<*>>): VarDecl = +fun Decl.changeVars(varLut: Map, VarDecl<*>>): Decl = + (varLut[this] as? Decl ?: this) + +fun VarDecl.changeVars(varLut: Map, VarDecl<*>>): VarDecl = (varLut[this] ?: this) as VarDecl + fun XcfaProcedureBuilder.canInline(): Boolean = canInline(LinkedList()) private fun XcfaProcedureBuilder.canInline(tally: LinkedList): Boolean { if (metaData["recursive"] != null) return false @@ -150,34 +158,4 @@ private fun XcfaProcedureBuilder.canInline(tally: LinkedList): Boolean { tally.pop() metaData[if (recursive) "recursive" else "canInline"] = Unit return !recursive -} - -internal fun XcfaLabel.removeUnusedWrites(unusedVars: Set>): XcfaLabel { - return when (this) { - is SequenceLabel -> { - val newLabels = mutableListOf() - this.labels.forEach { label -> - val newLabel = label.removeUnusedWrites(unusedVars) - if (newLabel !is NopLabel) newLabels.add(newLabel) - } - SequenceLabel(newLabels) - } - - is NondetLabel -> { - val newLabels = mutableSetOf() - this.labels.forEach { label -> - val newLabel = label.removeUnusedWrites(unusedVars) - if (newLabel !is NopLabel) newLabels.add(newLabel) - } - NondetLabel(newLabels) - } - - is StmtLabel -> when (this.stmt) { - is AssignStmt<*> -> if (unusedVars.contains(this.stmt.varDecl)) NopLabel else this - is HavocStmt<*> -> if (unusedVars.contains(this.stmt.varDecl)) NopLabel else this - 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 da368e558a..570ab2b126 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 @@ -66,8 +66,8 @@ class PassTests { PassTestData( global = { "x" type Int() init "0"; "y" type Int() init "0" }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext) + NormalizePass(), + DeterministicPass() ), input = { (init to final) { @@ -89,9 +89,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - EliminateSelfLoops(parseContext), + NormalizePass(), + DeterministicPass(), + EliminateSelfLoops(), LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_SEQ }, ), input = { @@ -112,9 +112,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - EliminateSelfLoops(parseContext), + NormalizePass(), + DeterministicPass(), + EliminateSelfLoops(), LbePass(parseContext).also { LbePass.level = LbePass.LbeLevel.LBE_FULL }, ), input = { @@ -145,8 +145,8 @@ class PassTests { PassTestData( global = { }, passes = listOf( - EmptyEdgeRemovalPass(parseContext), - UnusedLocRemovalPass(parseContext) + EmptyEdgeRemovalPass(), + UnusedLocRemovalPass() ), input = { (init to "L1") { @@ -171,9 +171,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - ErrorLocationPass(false, parseContext) + NormalizePass(), + DeterministicPass(), + ErrorLocationPass(false) ), input = { (init to final) { @@ -187,10 +187,10 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - FinalLocationPass(false, parseContext), - UnusedLocRemovalPass(parseContext) + NormalizePass(), + DeterministicPass(), + FinalLocationPass(false), + UnusedLocRemovalPass() ), input = { (init to "L1") { @@ -235,28 +235,28 @@ class PassTests { (init to "L1") { "x".assign("0") } - ("L1" to "loop0_L2") { + ("L1" to "L2_loop0") { nop() "x".assign("(+ x 1)") } - ("loop0_L2" to "loop0_L1") { + ("L2_loop0" to "L1_loop0") { skip() } - ("loop0_L1" to "loop1_L2") { + ("L1_loop0" to "L2_loop1") { nop() "x".assign("(+ x 1)") } - ("loop1_L2" to "loop1_L1") { + ("L2_loop1" to "L1_loop1") { skip() } - ("loop1_L1" to "loop2_L2") { + ("L1_loop1" to "L2_loop2") { nop() "x".assign("(+ x 1)") } - ("loop2_L2" to "loop2_L1") { + ("L2_loop2" to "L1_loop2") { skip() } - ("loop2_L1" to final) { + ("L1_loop2" to final) { nop() } }, @@ -269,8 +269,8 @@ class PassTests { }; }, passes = listOf( - NormalizePass(fpParseContext), - DeterministicPass(fpParseContext), + NormalizePass(), + DeterministicPass(), FpFunctionsToExprsPass(fpParseContext), ), input = { @@ -336,15 +336,15 @@ class PassTests { } (init to final) { "y".assign( - "(ite (isinfinite x) #b00000000000000000000000000000000 #b00000000000000000000000000000001)") + "(ite (or (isinfinite x) (fpisnan x)) #b00000000000000000000000000000000 #b00000000000000000000000000000001)") } }, ), PassTestData( global = { "x" type Int() init "0"; "y" type Int() init "0"; }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), + NormalizePass(), + DeterministicPass(), HavocPromotionAndRange(parseContext), ), input = { @@ -362,8 +362,8 @@ class PassTests { PassTestData( global = { "x" type Int() init "0"; "y" type Int() init "0"; }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), + NormalizePass(), + DeterministicPass(), HavocPromotionAndRange(parseContext), ), input = { @@ -386,10 +386,10 @@ class PassTests { PassTestData( global = { "x" type Int() init "0" }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - RemoveDeadEnds(parseContext), - UnusedLocRemovalPass(parseContext) + NormalizePass(), + DeterministicPass(), + RemoveDeadEnds(), + UnusedLocRemovalPass() ), input = { (init to "L1") { @@ -423,9 +423,9 @@ class PassTests { PassTestData( global = { "x" type Int() init "0"; "thr1" type Int() init "0" }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - CLibraryFunctionsPass(parseContext), + NormalizePass(), + DeterministicPass(), + CLibraryFunctionsPass(), ), input = { (init to "L1") { @@ -459,9 +459,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - SvCompIntrinsicsPass(parseContext), + NormalizePass(), + DeterministicPass(), + SvCompIntrinsicsPass(), ), input = { (init to "L1") { @@ -483,9 +483,9 @@ class PassTests { PassTestData( global = { "x" type Int() init "0"; "thr1" type Int() init "0" }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - NondetFunctionPass(parseContext) + NormalizePass(), + DeterministicPass(), + NondetFunctionPass() ), input = { (init to "L1") { @@ -501,9 +501,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - UnusedVarPass(parseContext, NullLogger.getInstance()) + NormalizePass(), + DeterministicPass(), + UnusedVarPass(NullLogger.getInstance()) ), input = { "tmp" type Int() @@ -514,9 +514,9 @@ class PassTests { PassTestData( global = { }, passes = listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), - EliminateSelfLoops(parseContext) + NormalizePass(), + DeterministicPass(), + EliminateSelfLoops() ), input = { ("L1" to "L1") { @@ -550,7 +550,7 @@ class PassTests { val x = Var("x", Int()) val y = Var("y", Int()) val xcfaLabel = { a: VarDecl, b: VarDecl -> - StmtLabel(Assign(a, b.ref), metadata = EmptyMetaData) + StmtLabel(Assign(a, b.ref)) } val x_prime = Var("x'", Int()) @@ -563,8 +563,8 @@ class PassTests { fun testInline() { val xcfaSource = xcfa("example") { procedure("main", ProcedurePassManager(listOf( - NormalizePass(parseContext), - DeterministicPass(parseContext), + NormalizePass(), + DeterministicPass(), InlineProceduresPass(parseContext)))) { (init to final) { "proc1"() 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 f3b50b01c1..77c48dc44e 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 @@ -52,13 +52,13 @@ class UtilsTest { StartLabel("", listOf(xPrime.ref), y, EmptyMetaData)), Arguments.of(ReturnLabel(JoinLabel(x, EmptyMetaData)), ReturnLabel(JoinLabel(xPrime, EmptyMetaData))), - Arguments.of(StmtLabel(Assign(x, y.ref), metadata = EmptyMetaData), - StmtLabel(Assign(xPrime, y.ref), metadata = EmptyMetaData)), - Arguments.of(StmtLabel(Havoc(x), metadata = EmptyMetaData), - StmtLabel(Havoc(xPrime), metadata = EmptyMetaData)), - Arguments.of(StmtLabel(Assume(Eq(x.ref, y.ref)), metadata = EmptyMetaData), - StmtLabel(Assume(Eq(xPrime.ref, y.ref)), metadata = EmptyMetaData)), - Arguments.of(StmtLabel(Skip(), metadata = EmptyMetaData), StmtLabel(Skip(), metadata = 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())), ) }