Skip to content

Commit 24a7df7

Browse files
authored
Fix set union performance (#226)
* Fix set union * Fix detekt issues
1 parent 76a8f23 commit 24a7df7

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

usvm-core/src/main/kotlin/org/usvm/collection/set/primitive/USymbolicSetUnionAdapter.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,25 @@ sealed class USymbolicSetUnionAdapter<
3535
override fun includesConcretely(key: DstKey) =
3636
includesSymbolically(key, composer = null).isTrue
3737

38+
private var lastIncludesSymbolicallyCheck: IncludesSymbolicallyCache<SrcKey>? = null
39+
3840
override fun includesSymbolically(key: DstKey, composer: UComposer<*, *>?): UBoolExpr {
3941
val srcKey = convert(key, composer)
40-
return setOfKeys.read(srcKey, composer)
42+
43+
/**
44+
* In the case of deep set union hierarchy we have multiple checks of the same key.
45+
* We can cache the last checked key to overcome this issue
46+
* */
47+
val prevIncludesSymbolicallyCache = lastIncludesSymbolicallyCheck
48+
if (prevIncludesSymbolicallyCache != null) {
49+
if (prevIncludesSymbolicallyCache.key == srcKey) {
50+
return prevIncludesSymbolicallyCache.result
51+
}
52+
}
53+
54+
return setOfKeys.read(srcKey, composer).also {
55+
lastIncludesSymbolicallyCheck = IncludesSymbolicallyCache(srcKey, it)
56+
}
4157
}
4258

4359
override fun isIncludedByUpdateConcretely(
@@ -49,6 +65,8 @@ sealed class USymbolicSetUnionAdapter<
4965
"(union $collection)"
5066

5167
abstract override fun collectSetElements(elements: USymbolicSetElementsCollector.Elements<DstKey>)
68+
69+
private data class IncludesSymbolicallyCache<Key>(val key: Key, val result: UBoolExpr)
5270
}
5371

5472
class UAllocatedToAllocatedSymbolicSetUnionAdapter<SetType, ElemSort : USort>(

usvm-core/src/test/kotlin/org/usvm/memory/SetEntriesTest.kt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
package org.usvm.memory
22

3+
import io.ksmt.expr.KBitVecValue
4+
import io.ksmt.sort.KBv32Sort
35
import io.mockk.every
46
import io.mockk.mockk
57
import org.junit.jupiter.api.BeforeEach
68
import org.usvm.Type
9+
import org.usvm.UBoolExpr
710
import org.usvm.UBv32SizeExprProvider
11+
import org.usvm.UBv32Sort
812
import org.usvm.UComponents
913
import org.usvm.UContext
1014
import org.usvm.UExpr
1115
import org.usvm.UHeapRef
1216
import org.usvm.USizeSort
17+
import org.usvm.UTransformer
1318
import org.usvm.api.refSetAddElement
1419
import org.usvm.api.refSetRemoveElement
1520
import org.usvm.api.setAddElement
1621
import org.usvm.api.setRemoveElement
22+
import org.usvm.apply
23+
import org.usvm.collection.set.primitive.USetEntryLValue
1724
import org.usvm.collection.set.primitive.setEntries
1825
import org.usvm.collection.set.primitive.setUnion
1926
import org.usvm.collection.set.ref.refSetEntries
@@ -23,6 +30,7 @@ import org.usvm.constraints.UEqualityConstraints
2330
import org.usvm.constraints.UTypeConstraints
2431
import org.usvm.isTrue
2532
import org.usvm.memory.key.USizeExprKeyInfo
33+
import org.usvm.regions.SetRegion
2634
import kotlin.test.Test
2735
import kotlin.test.assertEquals
2836
import kotlin.test.assertTrue
@@ -104,4 +112,58 @@ class SetEntriesTest {
104112
val definitelyInSet = setEntries.entries.filter { heap.read(it).isTrue }.mapTo(hashSetOf()) { it.setElement }
105113
assertEquals(expectedDefInSet, definitelyInSet)
106114
}
115+
116+
private object SetBv32ElementInfo :
117+
USymbolicCollectionKeyInfo<UExpr<UBv32Sort>, SetRegion<KBitVecValue<KBv32Sort>>> {
118+
override fun mapKey(key: UExpr<UBv32Sort>, transformer: UTransformer<*, *>?): UExpr<UBv32Sort> =
119+
transformer.apply(key)
120+
121+
override fun eqSymbolic(ctx: UContext<*>, key1: UExpr<UBv32Sort>, key2: UExpr<UBv32Sort>): UBoolExpr =
122+
ctx.mkEq(key1, key2)
123+
124+
override fun eqConcrete(key1: UExpr<UBv32Sort>, key2: UExpr<UBv32Sort>): Boolean =
125+
key1 == key2
126+
127+
override fun keyToRegion(key: UExpr<UBv32Sort>): SetRegion<KBitVecValue<KBv32Sort>> =
128+
if (key is KBitVecValue<UBv32Sort>) {
129+
SetRegion.singleton(key)
130+
} else {
131+
SetRegion.universe()
132+
}
133+
134+
override fun topRegion(): SetRegion<KBitVecValue<KBv32Sort>> = SetRegion.universe()
135+
override fun bottomRegion(): SetRegion<KBitVecValue<KBv32Sort>> = SetRegion.empty()
136+
137+
override fun cmpSymbolicLe(ctx: UContext<*>, key1: UExpr<UBv32Sort>, key2: UExpr<UBv32Sort>) = error("unused")
138+
override fun cmpConcreteLe(key1: UExpr<UBv32Sort>, key2: UExpr<UBv32Sort>) = error("unused")
139+
override fun keyRangeRegion(from: UExpr<UBv32Sort>, to: UExpr<UBv32Sort>) = error("unused")
140+
}
141+
142+
@Test
143+
fun testSetUnion(): Unit = with(ctx) {
144+
val maxElement = 100
145+
val sets = List(maxElement + 1) { idx ->
146+
val setRef = mkConcreteHeapRef(addressCounter.freshAllocatedAddress())
147+
val setElement = mkBv(idx)
148+
heap.setAddElement(setRef, setElement, setType, SetBv32ElementInfo, trueExpr)
149+
setRef
150+
}
151+
152+
sets.reduce { left, right ->
153+
heap.setUnion(left, right, setType, bv32Sort, SetBv32ElementInfo, trueExpr)
154+
right
155+
}
156+
157+
val resultSet = sets.last()
158+
159+
for (i in 0..maxElement) {
160+
val lvalue = USetEntryLValue(bv32Sort, resultSet, mkBv(i), setType, SetBv32ElementInfo)
161+
val elementContains = heap.read(lvalue)
162+
assertEquals(trueExpr, elementContains)
163+
}
164+
165+
val lvalue = USetEntryLValue(bv32Sort, resultSet, mkBv(maxElement + 1), setType, SetBv32ElementInfo)
166+
val elementContains = heap.read(lvalue)
167+
assertEquals(falseExpr, elementContains)
168+
}
107169
}

0 commit comments

Comments
 (0)