2016 Complete Symbolic Simulation of SystemC Models Efficient Formal Verification of Finite Non-Terminating Programs
2016 Complete Symbolic Simulation of SystemC Models Efficient Formal Verification of Finite Non-Terminating Programs
Springer awards „Best Masters“ to the best master’s theses which have been com-
pleted at renowned universities in Germany, Austria, and Switzerland.
The studies received highest marks and were recommended for publication by su-
pervisors. They address current issues from various fields of research in natural
sciences, psychology, technology, and economics.
The series addresses practitioners as well as scientists and, in particular, offers guid-
ance for early stage researchers.
Vladimir Herdt
Complete Symbolic
Simulation of
SystemC Models
Efficient Formal Verification of Finite
Non-Terminating Programs
With a Preface by Prof. Dr. Rolf Drechsler
Vladimir Herdt
Bremen, Germany
BestMasters
ISBN 978-3-658-12679-7 ISBN 978-3-658-12680-3 (eBook)
DOI 10.1007/978-3-658-12680-3
Springer Vieweg
© Springer Fachmedien Wiesbaden 2016
This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or
part of the material is concerned, speci¿cally the rights of translation, reprinting, reuse of illus-
trations, recitation, broadcasting, reproduction on micro¿lms or in any other physical way, and
transmission or information storage and retrieval, electronic adaptation, computer software, or by
similar or dissimilar methodology now known or hereafter developed.
The use of general descriptive names, registered names, trademarks, service marks, etc. in this
publication does not imply, even in the absence of a speci¿c statement, that such names are
exempt from the relevant protective laws and regulations and therefore free for general use.
The publisher, the authors and the editors are safe to assume that the advice and information in this
book are believed to be true and accurate at the date of publication. Neither the publisher nor the
authors or the editors give a warranty, express or implied, with respect to the material contained
herein or for any errors or omissions that may have been made.
Electronic systems consist of a large number of interacting hardware- and software compo-
nents. Only the hardware often is assembled of more than a billion transistors. To cope with
this increasing complexity system description languages, which allow modeling at high level
of abstraction, have been introduced and are focus of current research. They facilitate architec-
tural exploration as well as Hardware/Software Co-Design. SystemC has become the de-facto
standard for modeling at the system level.
The SystemC model serves as reference for subsequent development steps. Errors in a Sys-
temC model are very critical, since they will propagate and become very costly. Thus, develop-
ing verification methods for SystemC is of very high importance. Existing formal verification
approaches at lower abstraction levels are already very sophisticated and require a profound
technical understanding to be further improved.
This book makes a significant contribution to the ongoing research activities. It is based on
the master thesis of Vladimir Herdt, which he has written as a student in the Group of Com-
puter Architecture (AGRA) at the University of Bremen, Germany.
Compared to existing approaches, the proposed method works more efficiently and thus can
verify much larger (non-terminating) systems. It works by extending symbolic simulation with
stateful exploration and efficiently handling repeating behavior in the presence of symbolic
values. Besides the carefully formulated theoretical presentation, the proposed method and
additional optimizations have been implemented and evaluated on relevant examples. The com-
prehensive evaluation clearly shows the advantages of the proposed approach.
The significance of the obtained scientific results is further confirmed by the publication “Veri-
fying SystemC using Stateful Symbolic Simulation”, which is based on the thesis and has been
accepted for presentation at Design Automation Conference (DAC) 2015, San Francisco, USA
– the most renowned conference for electronic system design.
List of Figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
List of Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
List of Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
List of Listings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii
Acronyms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix
1. Introduction 1
1.1. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Verification of SystemC Designs . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.1. Simulation-Based Methods . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.2. Formal Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3. Thesis Goal: Complete Symbolic Simulation . . . . . . . . . . . . . . . . . . 3
1.4. Thesis Contributions and Organization . . . . . . . . . . . . . . . . . . . . . . 4
2. Preliminaries 7
2.1. SystemC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1. Simulation Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2. SystemC Intermediate Verification Language . . . . . . . . . . . . . . . . . . 9
2.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. Symbolic Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4. State Transition System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.1. Modeling IVL Programs as STS . . . . . . . . . . . . . . . . . . . . . 13
2.4.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4.3. Remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5. Basic Stateful Model Checking Algorithm . . . . . . . . . . . . . . . . . . . . 16
2.6. Partial Order Reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.6.1. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.6.2. Computing Static Persistent Sets . . . . . . . . . . . . . . . . . . . . . 22
2.6.3. Dynamic Partial Order Reduction . . . . . . . . . . . . . . . . . . . . 26
7. Experiments 97
7.1. Configuration Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
7.2. Benchmark Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
7.3. Comparing Hashing Methods for Symbolic State Parts . . . . . . . . . . . . . 101
7.4. Comparing SMT Solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Contents ix
8. Conclusion 117
8.1. Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
A. Appendix 121
A.1. Generating Consistent Equality Assumptions . . . . . . . . . . . . . . . . . . 121
A.1.1. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
A.1.2. First Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
A.1.3. Second Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
A.2. POR Correctness Proof: Deadlock Preserving . . . . . . . . . . . . . . . . . . 131
A.3. POR Correctness Proof: Assertion Violations Preserving . . . . . . . . . . . . 133
A.3.1. Proof of Theorem A.3.1 . . . . . . . . . . . . . . . . . . . . . . . . . 134
A.4. SSR without POR Correctness Proof: Assertion Violation Preserving . . . . . . 136
A.5. SSR with POR Correctness Proof: Assertion Violations Preserving . . . . . . . 137
A.6. Reduction Function Condition Proofs . . . . . . . . . . . . . . . . . . . . . . 139
A.7. Refined Assertion Violation Preserving Exploration . . . . . . . . . . . . . . . 140
A.8. SDPOR Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
A.9. Solving the Ignoring Problem implicitly . . . . . . . . . . . . . . . . . . . . . 145
A.9.1. Static Partial Order Reduction . . . . . . . . . . . . . . . . . . . . . . 145
A.9.2. Dynamic Partial Order Reduction . . . . . . . . . . . . . . . . . . . . 147
A.9.3. Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
A.10.Correctness Proof of the Exact Symbolic Subsumption . . . . . . . . . . . . . 148
A.10.1. Preliminary Part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
A.10.2. Main Part . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Bibliography . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
List of Figures
4.1. Example scheduling that may be performed by the stateless DPOR algorithm for
the program in Listing 4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.2. Relevant part of the complete state space for the exploration of the program Listing
4.1 following the execution order as shown in Figure 4.1 . . . . . . . . . . . . . . 42
4.3. Example state space that the A-SDPOR algorithm could explore for the program in
Listing 4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.4. Part of an abstract state space . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.1. Complete partial order reduced state space for the program in Listing 6.1 . . . . . . 71
6.2. Complete state space of the tokenring example program in Listing 6.6 . . . . . . . 81
6.3. Complete partial order reduced state space for the program in Listing 6.7 for three
time steps (0,1,2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.4. The precision of different state matching methods depicted as partial order (CEQ is
the weakest and ESS the strongest method) . . . . . . . . . . . . . . . . . . . . . 91
6.5. Path condition simplification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.6. Structural expression matching example . . . . . . . . . . . . . . . . . . . . . . . 95
2.1. Relevant informations when applying the DPOR algorithm to the program in List-
ing 2.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
6.1. Relevant state parts for the detection of the equivalent states in program Listing 6.7
as shown in Figure 6.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.1. Example program that appeared in the preliminaries as Listing 2.8 to demonstrate
the basic DPOR algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.2. Example program to demonstrate that the A-SDPOR algorithm is unsound for
cyclic state spaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7.1. term-counter, example program that generates many states with equal concrete state
parts but different symbolic state parts . . . . . . . . . . . . . . . . . . . . . . . . 101
Acronyms
IB Induction Base
IS Induction Step
IH Induction Hypothesis
1.1. Motivation
System-on-Chips (SoC) are ubiquitous nowadays. They integrate an increasingly large num-
ber of hardware and software components on a single chip. The development of such complex
systems is very challenging, especially within todays tight time-to-market constraints. To cope
with this rising complexity, the level of abstraction is raised beyond the Register Transfer Level
(RTL) to the Electronic System Level (ESL) [BMP07]. A higher level of abstraction allows for
easier exploration of design alternatives and facilitates the parallel development and integration
of hardware and software components. SystemC has become the de-facto standard in the ESL
design [GD10]. Technically, it is a C++ library providing standardized components that facili-
tate the development of electronic systems. The structure of a SystemC design is described in
terms of modules, ports, interfaces and channels, while the behaviour is described in processes.
An event-driven simulation kernel is provided which executes the processes non-preemptively
in an arbitrary order. SystemC designs are developed using a behavioral/algorithmic style in
combination with abstract communication based on Transaction Level Modeling (TLM). Since
the SystemC design serves as initial (executable) reference for subsequent development steps,
ensuring its correctness becomes crucial, especially for safety critical systems, as undetected
errors will propagate and become very costly.
schedulings.
The approaches [Hel+06; HMM09] start by exploring a random scheduling. Based on the
observed process interactions, alternate schedulings are explored. A method based on DPOR
is employed to avoid the exploration of equivalent schedulings. [KGG08] first employs a static
analysis to infer transition dependencies. A transition of a process is essentially a list of state-
ments that is executed without interruption, due to the non-preemptive scheduling semantics of
the SystemC simulation kernel. The statically pre-computed informations are queried at run-
time by a DPOR based method to calculate process dependencies based on the dynamically
explored paths. [BK10] uses model checking techniques to compute precise process dependen-
cies during a preliminary static analysis. Then the SystemC program is transformed into a C++
program, which can be normally compiled and executed. The SystemC scheduler and the stati-
cally pre-computed race conditions are embedded into the C++ program to explore all relevant
schedulings effectively.
Albeit providing better coverage, these methods still need representative inputs and thus can-
not guarantee the absence of errors. Formal methods are required to ensure correctness of a
design. They can either prove mathematically that a specific property holds or provide a counter
example that shows a violation.
• STATE, first proposed in [HFG08], translates SystemC designs to timed automata. With
STATE it is not possible to verify properties on SystemC designs directly. Instead, they
have to be formulated on the automata and can then be checked using the UPPAAL model
checker [Amn+01]. Although this approach is complete, it is potentially inefficient as UP-
PAAL is not geared toward dealing with large input spaces, as shown by the experimental
evaluation in [Le+13].
• SCIVER [GLD10] translates SystemC designs into sequential C models first. Tempo-
ral properties using an extension of PSL [Tab+08] can be formulated and integrated into
the C model during generation. Then, C model checkers can be applied to check for as-
sertion violations. High-level induction on the generated C model has been proposed to
achieve completeness and efficiency. However, no dedicated techniques to prune redun-
dant scheduling sequences are provided.
• Kratos [CNR13; Cim+11] translates SystemC designs into threaded C models. Then,
the ESST algorithm is employed, which combines an explicit scheduler and symbolic
lazy abstraction [Cim+10]. POR techniques are also integrated into the explicit scheduler
[CNR11]. For property specification, simple C assertions are supported. Although this
1 Part of this description already appeared in [Le+13].
1.3. Thesis Goal: Complete Symbolic Simulation 3
approach is complete, its potentially slow abstraction refinements may become a perfor-
mance bottleneck.
• SDSS [Cho+12] formalizes the semantics of SystemC designs in terms of Kripke struc-
tures. Then, Bounded Model Checking (BMC) and induction can be applied in a similar
manner as in SCIVER. The main difference is that the scheduler is not involved in the en-
coding of SDSS. It is rather explicitly executed to generate an SMT formula that covers
the whole state space. Still, no dedicated techniques to handle equivalent scheduling se-
quences are supported. This limitation is removed in [CCH13] by employing a so called
symbolic POR. The symbolic simulation engine is used to precisely capture transition in-
dependencies, which are then used to dynamically infer process dependencies. A heuristic
based on [KWG09] is employed to prune redundant scheduling interleavings during the
state space exploration. Furthermore symbolic state merging is used to combine multiple
execution paths to reduce the number of explored paths. Although this recent extension is
quite efficient (an advantageous comparison with Kratos is also provided), it is incomplete
and can only prove assertions in terminating SystemC programs.
• [Le+13] proposes an Intermediate Verification Language (IVL) for SystemC. Basically
the (SystemC) IVL is a compact language that can be much more easily processed and
analyzed, yet it is sufficient to describe the behaviour of SystemC programs. The inten-
tion of the IVL is to clearly separate the tasks of a SystemC front-end and a SystemC
back-end. Furthermore a symbolic simulator called SISSI has been proposed to verify
IVL programs. In particular SISSI can discover or prove the absence of assertion vio-
lations and other types of errors such as division by zero or memory access violation2 .
SISSI employs symbolic simulation, a combination of complete scheduling exploration
and symbolic execution [Kin76; CDE08], to exhaustively explore the state space. SPOR
and DPOR are used to prune redundant schedulings. Experimental evaluation in [Le+13]
shows that SISSI outperforms the other listed approaches (with the exception of SDSS,
which is not available for evaluation). However, a major limitation of SISSI is its in-
completeness, due to the core algorithm that performs a stateless search, i.e. no record
of visited states is kept. Consequently, it cannot avoid re-exploration of already visited
states, and thus can only be applied to models that either terminate or contain bugs, and
is unable to verify assertions over cyclic state spaces.
unsoundness, i.e. assertion violations can be missed. This is due to the (transition/action)
ignoring problem, which refers to a situation, where a relevant transition is not explored.
Furthermore, in the case of DPOR, additional care needs to be taken when calculating
process dependencies, in order to preserve soundness. This situation, where a relevant
dependency is missed, will be referred to as the missing dependency problem in this the-
sis. This problem can arise in both acyclic and cyclic state spaces.
2. Similarly, the efficient combination of the existing symbolic execution with a stateful
search is challenging. A stateful search requires a process called state matching to de-
cide whether a state has already been visited to avoid re-exploration. Symbolic execution
stores and manipulates symbolic expressions, which represent sets of concrete values.
Therefore, symbolic state matching is required which involves comparison of different
symbolic expressions. The problem is that symbolic expressions can be represented in
many (actually infinitely) different ways, e.g. the expressions a + a and 2 * a are struc-
turally different (hence not equal) but semantically equivalent.
4. Finally, POR and symbolic state matching can be employed together as complementary
reduction techniques. Nonetheless, additional care needs to be taken to avoid unsound-
ness.
These challenges require non-trivial solutions, including rigorous formalization to prove their
soundness.
• A stateful search with SPOR is implemented. A cycle proviso is integrated to solve the
ignoring problem. More details on the ignoring problem in the context of SystemC, a
theoretical discussion of the correctness of this approach and the actual implementation
are presented in Chapter 3.
• The stateless DPOR algorithm is extended to a stateful algorithm in three steps. The
first step provides support for acyclic state spaces. This extension is adapted from the
existing work [Yan+08] to accommodate the SystemC specific simulation semantics. In
the second step a novel extension is proposed to handle cyclic state spaces. Finally, in the
third step, a cycle proviso is integrated to solve the ignoring problem in general. These
extensions will be further discussed in Chapter 4.
• Different heuristics for more efficient state subsumption detection are proposed in Chapter
6 as alternatives to the exact algorithm. They include explicit and solver-based methods
with different configurations, to balance between precision and runtime overhead. The
goal is to increase the overall scalability, by spending less time in state matching.
The necessary theoretical background for the contributions will be presented in Chapter 2.
The overall complete symbolic simulation approach of this thesis is evaluated in Chapter 7. As
part of the experiments it is compared against the latest version of Kratos [CNR13]. The results
show the efficacy and potential of the proposed complete symbolic simulation. The conclusion
and some ideas for future work are presented in Chapter 8. The Appendix (Chapter A) contains
proofs and some additional informations, that will be referenced from within the thesis.
Related Work The related work with respect to SystemC verification has already been dis-
cussed in Section 1.2. Here only related work directly connected to the contributions of this
thesis is considered.
The ignoring problem has first been identified in [Val89]. The solution is to impose an addi-
tional condition called a cycle proviso/condition. Different cycle provisos have been proposed
since then in combination with different search strategies [God96; HGP92; BH05; BLL06;
EP10]. In this thesis the cycle proviso proposed in [EP10] is used to solve the ignoring problem
in the SystemC context, when using POR in combination with a basic stateful search. How-
ever, this proviso is unsuitable when POR is combined with SSR, as it is too restrictive. Thus
a weaker proviso is proposed in this thesis, to preserve safety properties when combining POR
and SSR. To the best of the authors knowledge, such a proviso has not yet been proposed.
DPOR has been first introduced in [FG05]. It works by exploring an arbitrary sequence of
transitions until completion and dynamically tracks relevant runtime informations to compute
dependencies. These are used to detect and explore alternate relevant interleavings of tran-
sitions. DPOR supports stateless exploration of acyclic state spaces. A stateful extension is
non trivial and can lead to unsoundness (under-approximation by missing relevant transitions)
when implemented naively. As already mentioned, this problem will be referred to as missing
dependency problem in this thesis. [Yan+08] have extended DPOR to support stateful explo-
ration of terminating multi-threaded programs. Thus, their extension does not support cyclic
state spaces. Another stateful DPOR extension has been proposed in [YWY06]. According to
the authors of [YWY06], their algorithm supports cyclic state spaces, but it is unclear whether
the ignoring problem has been solved. Due to the special simulation semantics of SystemC,
these approaches are not directly applicable. To the best of the authors knowledge, DPOR has
only been provided in a stateless version so far [KGG08; Hel+06; HMM09] in the context of
SystemC. The stateful DPOR (SDPOR) implementation proposed in this thesis combines the
stateless DPOR algorithm for SystemC, as presented in [KGG08], with the approach presented
in [Yan+08] to support acyclic state spaces. To support cyclic state spaces a novel method is
proposed, instead of adapting and incorporating the approach proposed in [YWY06]. Compared
to [YWY06], the proposed approach is more lightweight.
6 1. Introduction
The subsumption detection between symbolic values, which is part of the implemented SSR,
is based on a method that appeared in [APV06] in the context of Java symbolic model checking.
They combine the method with additional abstractions, that result in under-approximations of
the normal behaviour, thus their combined approach is used for bug hunting.
2. Preliminaries
This chapter provides necessary background regarding SystemC, the IVL, symbolic execution,
POR and basic definitions for state transition system, which can be used to model finite SystemC
programs. Some parts of the SystemC and IVL description in Section 2.1 and Section 2.2
already appeared in [Le+13].
2.1. SystemC
SystemC is implemented as a C++ class library. It includes an event-driven simulation kernel
and provides common building blocks to facilitate the development of embedded systems.
The structure of a SystemC design is described with ports and modules, whereas the behav-
ior is described in processes which are triggered by events and communicate through channels.
SystemC provides three types of processes with SC THREAD being the most general type,
i.e. the other two can be modeled by using SC THREAD. A process gains the runnable sta-
tus when one or more events of its sensitivity list have been notified. The simulation kernel
selects one of the runnable processes and executes this process non-preemptively. The kernel
receives the control back if the process has finished its execution or blocks itself by executing
a context switch. A context switch is either one of the function calls wait(event), wait(time),
suspend(process). They will be briefly discussed in the following. Basically SystemC offers the
following variants of wait and notify for event-based synchronization [GLD10; IEE11]:
• wait(event) blocks the current process until the notification of the event.
• notify(event, delay) performs a timed notification of the event. It is called a delta notifi-
cation if the delay is zero. In this case the notification will be performed in the next delta
phase, thus a process waiting for the event becomes runnable in the next delta cycle.
• wait(delay) blocks the current process for the specified amount of time units. This oper-
ation can be equivalently rewritten as the following block { sc event e; notify(e,
delay); wait(e); }, where e is a unique event. Thus the wait(delay) variant will not
be further considered in the following.
More informations on immediate-, delta- and timed-notifications will be presented in the next
section which covers the simulation semantics of SystemC.
Additionally, the suspend(process) and resume(process) functions can be used for synchro-
nization. The former immediately marks a process as suspended. A suspended process is not
runnable. The resume function unmarks the process again. It is a form of delta notification, thus
its effects are postponed until the next delta phase of the simulation. Suspend and resume are
complementary to event-based synchronization. Thus a process can be suspended and waiting
for an event at the same time. In order to become runnable again, the process has to be resumed
again and the event has to be notified.
Simulation
Initialization
simulation
start
Elaboration Update
Delta Notify
simulation
re-start
Evaluation
no process
runnable
Notication at least
one process no process
runnable runnable
Timed
Update Cleanup
Notication
no process
runnable
Delta Notify
Figure 2.1.: Execution phases of a SystemC program. The notification phase is defined in this thesis as
additional phase to group the update, delta notify and timed notification phases.
The non-determinism in the evaluation phase of the simulation, due to multiple process
scheduling alternatives, is one of the reasons that give rise to the state explosion problem when
it comes to the verification of SystemC programs. In order to assure that no failure is missed
during simulation, it becomes necessary2 to explore all relevant scheduling orders.
Remark. In the following the update, delta notify and timed notification phases will often be
grouped as notification phase. Thus if simulation moves from the evaluation phase to the update
phase, it will be said that the simulation is in the notification phase.
The interested reader is referred to [GD10; Gro02; Bla+09] or the IEEE standard [IEE11] for
more details on SystemC.
design behavior to the IVL. Separating the verification process of SystemC programs into two
independent tasks makes both of them more manageable. In the following the structure and key
components of the IVL are briefly discussed.
Based on the SystemC simulation semantics described in the previous section, three basic
components of the SystemC kernel can be identified: SC THREAD, sc event and channel
update. These are adopted to be kernel primitives of the IVL: thread, event and update, respec-
tively. Associated to them are the following primitive functions in the IVL:
• wait and notify to wait for and notify an event (the notification can be either immediate
or delayed depending on the function arguments, similar to the corresponding functions
in SystemC)
These primitives form the backbone of the kernel. Other SystemC constructs such as sc signal,
sc mutex, static sensitivity, etc. can be modeled using this backbone. The behavior of a thread
or an update is defined by a function. Functions which are neither threads nor updates can
also be declared. Every function possesses a body which is a list of statements. A statement
is either an assignment, (conditional) goto statements or function call. Every structural control
statement (if-then-else, while-do, switch-case, etc.) can be mapped to conditional goto
statements (this task should also be performed by the front-end). Therefore, the representation
of a function body as a list of statements is general and at the same time much more manage-
able for a back-end. As data primitives the IVL supports boolean and integer data types of C++
together with all arithmetic and logic operators. Furthermore, arrays and pointers of primitive
types are also supported. For verification purpose, the IVL provides the assert and assume
functions. Symbolic values of integer types and arrays are also supported.
2.2.1. Example
Basically an IVL program consists of a list of declarations. These include functions, threads,
global variables and events. The execution of an IVL program starts by evaluating all global
variable declarations. Then the (unique) main function will be executed. The start statement3
starts the actual simulation. An optional maximum simulation time can be passed as argument.
If none or a negative value is passed, the simulation will not be time bounded. The simulation
semantics directly correspond to those of SystemC, as described in Section 2.1.1.
Remark. The syntax for the main function and threads is slightly different to normal functions,
since they neither take arguments nor return a result. Internally all of them are represented as
functions though.
In the following an example SystemC and corresponding IVL program are presented. The
main purpose of the example is to demonstrate some elements of the IVL. The example ap-
peared in similar form in [Le+13]. It is presented here for convenience. For the sake of clarity,
in this example and also in the following high level control structures are used in IVL programs
instead of (conditional) gotos. Some slight syntactic adaptions have been performed to make
the IVL easier to parse and read.
3 One can think of it as a function too.
2.3. Symbolic Execution 11
SystemC example Listing 2.1 shows a simple SystemC example. The design has one mod-
ule and three SC THREADs A, B and C. Thread A sets variable a to 0, if x is divisible by 2, and
to 1 otherwise (line 13-16). Variable x is initialized with a random integer value on line 6 (i.e. it
models an input). Thread B waits for the notification of event e and sets b = x / 2 subsequently
(line 20-21). Thread C performs an immediate notification of event e (line 25). If thread B is
not already waiting for it, the notification is lost. After the simulation the value of variable a
and b should be x % 2 and x / 2, respectively. Thus the assertion (2 ∗ b + a == x) is expected to
hold (line 32). Nevertheless, there exist counter-examples, for example the scheduling sequence
CAB leads to a violation of the assertion. The reason is that b has not been set correctly due to
the lost notification.
IVL example Listing 2.2 shows the same example in IVL. As can be seen the SystemC
module is ”unpacked”, i.e. variables, functions, and threads of the module are now global dec-
larations. The calls to wait and notify are directly mapped to statements of the same name.
Variable x is initialized with a symbolic integer value (line 2) and can have any value in the
range of unsigned int. The statement start on line 24 starts the simulation.
execution allows to store and manipulate both symbolic and concrete values. A symbolic value
can represent all or a subset of possible concrete value for each state part in the program. Thus
it can be used to exhaustively check a single execution path for some or all input data. During
execution a path condition pc is managed for every path. This is a Boolean expression that is
initialized as pc = True. It represents constraints that the symbolic values have to satisfy for
the corresponding path, thus effectively selecting the possible values a symbolic expression can
evaluate to.
There are basically two different ways to extend a path condition: either by adding an as-
sumption or executing a conditional goto. In order to add an assumption c, which itself is just
a boolean expression, to the current path condition pc, e.g. by executing an assume statement,
the formula pc ∧ c will be checked for satisfiability by e.g. an SMT solver. If it is satisfiable,
the path condition is update as pc := pc ∧ c. Otherwise the current execution path is considered
unfeasible and will be terminated.
When a conditional goto with branch condition c is executed in a state s, which represents an
execution path, an i.e. SMT solver is used to determine which of the branch condition and its
negation is satisfiable with the current path condition. If both are satisfiable, which means both
branches are feasible, then the execution path s is forked into two independent paths. One that
will take the goto sT and one that will not sF . The path conditions of both paths are updated
accordingly as pc(sT ) := pc(sT ) ∧ c and pc(sF ) := pc(sF ) ∧ ¬c respectively.
The symbolic execution effectively creates a tree of execution paths where the path condition
represents the constraints under which a specific position in the program will be reached. In
order to check whether an assertion of condition c is violated, the formula pc ∧ ¬c, where pc is
the path condition under which the assertion is reachable, will be checked for satisfiability. The
assertion is violated iff the formula is satisfiable.
The combination of symbolic execution with complete exploration of all possible process
scheduling sequences enables exhaustive exploration of state spaces. The combined approach
is called symbolic simulation. It is used by the symbolic simulator SISSI [Le+13] to discover
assertion violations and other types of errors, such as memory access errors in SystemC IVL
programs, or prove that none of them exists.
s0 s1
tE B1
A1
se
B1 tN B2 tN tC
s2 s3 s4 s5 s6 s7
Figure 2.2.: Complete state space for the program in Listing 2.3
s5 s6 s7 s8 s9
A1 tN B2 A2
B1
s0
A1
B1 tN B2
s1 s2 s3 s4
Figure 2.3.: Simplified state space for the program in Listing 2.3
transition in each state s, denoted as next(s, T ). This transition can either be enabled or disabled
in s. Basically thats the transition that will be executed next when thread T is selected for ex-
ecution in state s. A transition is enabled in a state s if it is the current active transition of a
runnable thread.
For verification purposes, the SystemC IVL supports the assume and assert statements.
Whenever an assertion violation is detected during the execution of a thread, the system will
reach the designated error state se . The assume statement can be handled similarly, by introduc-
ing a designated terminal state, that is reached whenever the assumed condition is unsatisfiable.
A notification transition changes the state of the system by performing the update, delta notify
and timed notification phases as necessary. The notification transition will be denoted as tN in
the following. A state s where tN ∈ en(s) is called a notification or notify state. According to
the simulation semantics of SystemC, a notification transition will only be enabled if no thread
transitions are runnable. Thus tN ∈ en(s) always implies that en(s) = {tN }. All transitions
between two notification states belong to the same delta cycle.
Thread and notification transitions are sufficient to model the simulation phase, which begins
with the start statement. The execution of statements before and after the start statement can be
modeled by introducing two additional distinguished transitions tE and tC respectively, similarly
to the notification phase transition tN . For the sake of simplicity these transitions will not be
further considered in the following. The next section provides an example to illustrate the
concepts of this section.
2.4.2. Example
Consider the simple IVL program in Listing 2.3. It consists of two threads A and B. Both of
them consist of two transitions, separated by context switches, called A1 ,A2 and B1 ,B2 respec-
2.4. State Transition System 15
1 int a = 0; 8 } 15 notify eA ;
2 int b = 0; 9 assert ( b == 0) ; 16 }
3 10 } 17
4 thread A { 11 18 main {
5 if ( b > 0) { 12 thread B { 19 start ;
6 wait eA ; 13 b = 1; 20 }
7 b = 0; 14 wait_time 0;
Listing 2.3: Example to demonstrate the correspondence between IVL programs and state transition sys-
tems
A1 = {5, 6, 9}
A2 = {7, 9}
B1 = {13, 14}
B2 = {15}
A graphical representation of the STS is shown in Figure 2.2. Circles denote normal states,
i.e. states where a transition can be selected, whereas squares denote states where the either one
of the designated transitions {tE ,tC ,tN } is explored. In the following the tE and tC transitions
will not be explicitly considered. Furthermore the last notification transition and unreachable
error state will also normally be omitted. A graphical representation incorporating these sim-
plifications is shown in Figure 2.3. Sometimes even the notification transitions in between will
be omitted, since they can be unambiguously inferred given the original program. Error states
will be shaded, as shown in Figure 2.2.
2.4.3. Remarks
This modeling requires not only that a SystemC program is finite but also that every transition
considered in isolation will terminate. For example consider the simple IVL program in Listing
2.5. It would be invalid according to the above definition, since the transition of the thread A
will not terminate, thus not reach a successor state. On the other hand a program with finite
cyclic state space as shown in Listing 2.4 is valid, since every transition will eventually either
finish execution or hit a context switch. While programs that loop without context switches
are very interesting and challenging in the context of formal verification of generic programs,
they are rather uncommon in the context of SystemC. For this reason such programs will not
be further considered, though the methods presented in this thesis can be extended to support
them.
4 Whether the transition A1 takes the branch in Line 5 depends on the state it is executed from.
16 2. Preliminaries
1 thread A { 1 thread A {
2 while ( true ) { 2 while ( true ) {
3 wait_time 0; 3 }
4 } 4 }
5 } 5
6 6 main {
7 main { 7 start ;
8 start ; 8 }
9 }
3 procedure explore(s) is
4 if s ∈
/ H then
5 H.add(s)
6 for t ∈ en(s) do
7 n ← succ(s,t)
8 explore(n)
the execution of these equivalent transition sequences in a single representative order, reducing
resource usage required for model checking.
Several similar approaches for POR have been developed. Most of them work by selecting
a subset of enabled transitions in each state, resulting in the exploration of a reduced state
space. Such sets are called persistent [God91; GP93; God96], stubborn [Val89; Val91; Val98;
VV98] or ample [Pel93; Cla+99]. Optionally sleep sets can be additionally used to select
smaller subsets of enabled transitions in each state, thus further reducing the explored state
space [GW93; God96]. The persistent set framework as presented in [God96] will be used in
this thesis.
The reduction is achieved on the fly during state space exploration to reduce the resource
usage. Constructing the complete state space in advance and then verify properties of interest
on the reduced part of the state space would defy the whole purpose of the reduction.
Two different approaches are commonly used to compute persistent sets for each state. Static-
and Dynamic Partial Order Reduction, denoted as SPOR and DPOR respectively. The former
is a classical approach, e.g. from [God96], that computes persistent sets whenever a state is
entered, based on some statically precomputed transition interference relations. The latter is a
more recent approach, first introduced in [FG05], that explores an arbitrary path until comple-
tion and dynamically tracks runtime informations to compute dependencies. These are used to
detect and explore alternate relevant interleavings of transitions. Ultimately, DPOR will also
have explored a persistent set from each state.
The advantage of DPOR is that the information at runtime are precise, this allows to com-
pute dependencies more accurately without too much over-approximation, resulting in a better
reduction. Especially when arrays and pointers are involved 5 . A disadvantage compared to
the static method is the additional runtime overhead to track runtime informations and compute
dependencies. This is necessary, since the actual statements executed by a transition depend on
the state where the transition is executed from. Also a DPOR algorithm does not necessarily
yield a better reduction than a SPOR algorithm. The achieved reduction depends on the actual
implementation.
Another notable advantage of SPOR compared to DPOR is that integration of complementary
techniques like stateful model checking, state subsumption matching and parallel execution
among others is easier with SPOR [Bok+11; YWY06; Yan+08; Yan+07; Sim+13]. The DPOR
reduction requires non-trivial extensions in each case to preserve the soundness of the combined
approach. To guarantee the soundness, often conservative assumptions are used thus reducing
the benefit of the higher precision of DPOR compared to SPOR. This issue will be further
discussed when presenting DPOR in Chapter 4 and State Subsumption Reduction (SSR) in
Chapter 5.
In the following some standard definitions with respect to POR are introduced. They include
the definition of reduced state spaces, transition interference, trace equivalence and persistent
sets. Then Section 2.6.2 presents algorithms to compute static persistent sets based on (stati-
cally) precomputed transition interference relations. Finally, a stateless DPOR in the context of
SystemC is introduced.
5 Even though the informations at runtime are precisely available, the computed dependencies does not necessarily
have to be precise, e.g. it is inherently complex to precisely decide whether two array accesses a[i] and a[k]
must overlap if i and k are symbolic integer values.
18 2. Preliminaries
2.6.1. Definitions
Reduced State Space
Partial order reduction explores only a subset of the complete state space that is sufficient to
verify all properties of interest. Most approaches work by selecting only a subset of enabled
transitions in each state. This can be formalized by means of a reduction function r, e.g. as
shown in [EP10; BLL06].
Definition 2 (Reduction Function)
A reduction function r for an STS A=(S, s0 , T , Δ, se ) is a mapping from S to 2T such that
r(s) ⊆ en(s).
When all enabled transitions are in r(s) for some state s, so en(s) = r(s), then no reduction
is provided for state s. The state s is said to be fully expanded. A transition t is postponed in s
if t ∈ en(s) \ r(s).
A reduced state space AR can be constructed by applying a reduction function r to each
reachable state s of another state space A. Doing so will yield a subset of enabled transitions
r(s) ⊆ en(s) for each state s. Only transitions t ∈ r(s) will be explored.
Definition 3 (Reduced STS)
Let A=(S, s0 , T , Δ, se ) be an STS. A reduced STS AR =(SR , s0R , TR , ΔR , seR ) can be defined
by means of a reduction function r as:
• s ∈ SR iff there exists an execution sequence s0 ...sn such that s = sn and ti ∈ r(s j ) for
j ∈ {1..n}
The reduced state space contains a subset of states and transitions of the original state space,
thus SR ⊆ S and ΔR ⊆ Δ. Normally the reduced state space AR is computed from the complete
state space AG .
t ∗ t
− R s , s →
Hereafter the notations s → − R s and s →
− R will sometimes be used to explicitly refer
to the reduced state space. Normally they will be omitted though, since it is clear from the
context to which state space they refer.
Selective Search
This section presents a simple algorithm that will explore all states of a reduced state space
AR with the reduction function r. Such a selective search forms the basis of all following
algorithms and might be more intuitive to use than the declarative definition of the reduced
state space. The algorithm is shown in Algorithm 2. It employs a stateful depth first search
to visit all reachable states6 . Already visited states are stored in the (hash-)set H to avoid re-
exploration. Compared to the basic stateful model checking algorithm shown in Algorithm 1,
successor states are generated by means of the reduction function r, which selects only a subset
6 Using a depth first search is just a design decision, because it allows for a memory efficient state space explo-
ration. Any search method could be used here. It is only relevant that the complete reduced state space is
explored.
2.6. Partial Order Reduction 19
3 procedure explore(s) is
4 if s ∈
/ H then
5 H ← H ∪s
6 for t ∈ r(s) do
7 n ← succ(s,t)
8 explore(n)
of enabled transitions for each reached state. Hence it is called a selective search [God96;
EP10].
Transition Interference
The idea of (in-)dependence of transitions is a common point of many POR algorithms. Intu-
itively, two transitions t1 and t2 are independent if they cannot disable each other and if they
commute in any state of the system [FG05; KGG08; EP10].
Definition 4 (Transition (In-)Dependence)
A transition independence relation is a irreflexive, symmetric relation I ⊆ T × T satisfy-
ing the following two conditions for each state s ∈ S in the complete state space AG and
transitions (t1 ,t2 ) ∈ I:
t
1. Enabledness: If t1 ,t2 ∈ en(s) and s −
→1
s then t2 ∈ en(s )
t t t t
→ s then s −−
2. Commutativity: If t1 ,t2 ∈ en(s) and s −−
12
→ s
21
By definition transitions that are not co-enabled are always independent, since no state s ∈ S
exists such that t1 ,t2 ∈ en(s). All transitions that cannot be proven independent are assumed to
be dependent.
Another useful transition interference relation is the can-enabling relation, denoted as ce,
which appeared in similar form in [Bok+11]. It is based on the concept of necessary enabling
transitions (NES) [God96]. A transition t1 can enable another transition t2 , if in at least one
state where t2 is disabled, executing t1 results in a state where t2 is enabled.
Definition 5 (Transition can-enable)
A transition can-enable relation is a irreflexive relation ce ⊆ T × T satisfying
t
ce ⊇ {(t1 ,t2 ) ∈ T × T | ∃s, s ∈ S : t1 ∈ en(s) and t2 ∈
/ en(s) and s −
→1
s and t2 ∈ en(s )}
These transition interference relations can either be computed before the exploration of the
state space on the basis of a static analysis of the program or can be dynamically inferred
20 2. Preliminaries
at runtime. The former approach is taken by a SPOR method, whereas the latter approach
is employed by a DPOR method. It is always safe to over-approximate the dependency and
can-enable relation, i.e. including pairs of non-interfering transitions. Though more precise
definitions of transition interference relations are a crucial part of every POR method, as they
are used as basis to compute the subset of enabled transitions that shall be explored for each
state. Thus more precise relations yield better reductions and can result in the exploration of a
smaller state space.
IVL specific transition interference The above definitions are generic, as no specific se-
mantic has been used. This section instantiates them to the concrete semantics of the IVL.
Two transitions t1 and t2 are dependent if they are co-enabled and either (a) access the same
memory location where at least one non-commutative write access is involved (b) or one of
them can immediately notify the other (c) or one of them can suspend the other. All of these
conditions lead to the result that t1 and t2 do not commute. Condition (c) additionally states
that one transition disables the other. For simplicity it simply is assumed that all transitions are
co-enabled. Condition (a) considers some bitvector operations like addition, multiplication and
logic operations to be commutative.
A transition t1 can enable another transition t2 if t1 and t2 are co-enabled in the same delta
cycle and t2 waits on an event e and t immediately notifies e. Again for simplicity it is simply
assumed that all transitions can be co-enabled in the same delta cycle. Doing so is always safe,
since it can only lead to over-approximation of the transition dependency relation.
Delta- and timed notifications can also enable transitions, but they are not considered for the
computation of the dependency and can-enable relation. The effects of such notifications are
postponed until the next delta cycle. The simulation can only proceed from one delta cycle to
the next, if a state is reached, where no enabled (runnable) transition is available. Consequently
all these transition sequences are equivalent. For this reason only notifications whose effects
happen during the same delta cycle are considered in the can-enable relation ce. It turns out
that only immediate notifications fall into this category.
The set of equivalent transition sequences for w will be denoted as [w] = {w ∈ T ∗ | w ≡ w}.
It is called the trace (equivalence) class of w. For example let w1 = t1t2t3 and w2 = t2t1t3
and let t1 be independent with t2 , then w1 ≡ w2 holds. Since ≡ is an equivalence relation by
definition, w1 ∈ [w2 ], w2 ∈ [w1 ] and [w1 ] = [w2 ] hold too. By successively permuting adjacent
independent transitions in w, one can obtain all transition sequences in the trace class [w]. Thus
a trace class is fully characterized by one of its members and a (in-)dependence relation between
transitions. By definition, all transition sequences in a given trace class contain the same number
of transitions (since they can be obtained from each other by swapping adjacent transitions).
2.6. Partial Order Reduction 21
Moreover every one of them will result in the same state, when executed from the same state,
as the following theorem establishes [God96].
Theorem 2.6.1
w w
Let s be a state in AG . If s −→
1
s1 and s −→
2
s2 in AG , and if [w1 ] = [w2 ], then s1 = s2 .
The reader is referred to [God96] for a proof of this theorem. In the following, the notation
[w]s will often be used to denote the trace equivalence class of w when executed from state s.
Remark. The term trace refers in this thesis to a sequence of transitions. Sometimes it will also
be called a path. In [Maz87; God96] the term trace refers to the complete equivalence class [w],
thus it includes (potentially) multiple sequences of transitions. In this thesis the equivalence
class [w] is referred to as either trace class or more commonly simply as trace equivalence
class.
Definition 7 (Partial Order)
A relation R ⊆ A × A on a set A is a partial order iff R is reflexive, antisymmetric and
transitive. A partial order R is also a total order if for all a1 , a2 ∈ A either (a1 , a2 ) ∈ R or
(a2 , a1 ) ∈ R.
The intersection of all linearizations of a partial order results again in the same partial order.
There is a correspondence between trace equivalence classes and partial orders of transition
occurrences. The set of transition sequences in the trace class is the set of all linearizations of
the partial order of transition occurrences [God96]. Partial orders and thus trace equivalence
classes can be naturally represented by a happens before relation, e.g. as shown in [KGG08;
FG05]:
Definition 9 (Happens Before)
Let w = t1 ..tn be a trace in AG . Let dep be a dependency relation between transitions. A
happens before relation w is the smallest binary relation on T such that:
2. w is transitively closed
Example Consider the set T = {t1 ,t2 ,t3 ,t4 } of transitions. Let the independence relation I
be defined as the symmetric closure of I0 = {(t2 ,t3 ), (t2 ,t4 )}. Thus all other transition pairs
are dependent. Let s be a state in AG and w = t1t2t3t1t2t4t1 a trace in AG from s. The trace
equivalence class with respect to I is defined as:
All traces in [w]s lead to the same result state from s. The trace w contains seven transition
occurrences of four different transitions. The occurrences can be explicitly named as wa =
a1 ..a7 . Thus e.g. a1 corresponds to the first occurrence of t1 , and a4 to the second occurrence of
22 2. Preliminaries
a2 a5
a1 a4 a7
a3 a6
Figure 2.4.: Partial order of transition occurrences for the trace w and independence relation I
t1 and so on. A partial order R that captures the (in-)dependencies of the transition occurrences
is the transitive, reflexive closure of P which is defined as:
P = {(a1 , a2 ), (a1 , a3 ), (a2 , a4 ), (a3 , a4 ), (a4 , a5 ), (a4 , a6 ), (a5 , a7 ), (a6 , a7 )}
Basically P encodes the dependencies between the transition occurrences. A graphical repre-
sentation, that omits reflexive and transitive edges, is shown in Figure 2.4. An edge denotes
that the two transition occurrences are dependent. The partial order also has four different
linearizations. They correspond exactly to the trace equivalence class [w]s , when replacing
transition occurrences with the actual transitions. Similarly the happens before relation of tran-
sition occurrences w corresponds with the partial order R obtained from P. It is defined as
w = {(i, j) | (ai , a j ) ∈ R}, thus e.g. 1 w 2, 1 w 7, 3 w 5, and so on.
Persistent Set
Intuitively a set of transitions T is persistent in a state s of AG , if starting from s and executing
only transitions not in T , will have no dependency to any transition in T . So whatever one
does from s while staying outside of T will not influence any transition in T . Consequently at
least one transition t ∈ T has to be executed from s or any state reachable from s to produce a
dependency with any transition in the persistent set T . Persistent sets are formally defined as
[God96]:
Definition 10 (Persistent Set)
A set T of transitions enabled in a state s is persistent in s iff, for all non empty sequences
of transitions
t t t
s = s1 →
1
s2 →
2
...sn →
n
sn+1
11 return done
The algorithm computes a set of transitions T that can be inductively defined. Initially T =
{ts } where ts is an arbitrary enabled transition in state s. Then ∀t ∈ T :
• t ∈ en(s) =⇒ dep[t] ⊆ T
• t∈
/ en(s) =⇒ en(s) ⊆ T
The algorithm then returns en(s) ∩ T , which is a persistent set in s. A concrete implementation
of this description is available in Algorithm 3.
Whenever a dependency to a disabled transition t is encountered, the algorithm aborts and just
returns a trivial persistent set in s (all enabled transitions in s). The reason is, that the algorithm
does not know, which transition may enable t. Thus it simply assumes that any transition could
do so. A simple example that demonstrates why it is necessary to include transitions which can
enable currently disabled transitions into the persistent set is given in Example 1. The algorithm
in the next section additionally makes use of the ce relation to compute non trivial persistent
sets even if a disabled transition is encountered.
Example 1. This example demonstrates why it is necessary to include transitions, which can
enable currently disabled transitions, into the persistent set. Not doing so can miss assertion
violations as will be shown in the following. Consider the program in Listing 2.6 It consists
of three threads A,B and D. Both threads A and B consists of a single transition which will
also simply be denoted as A and B. Thread D consists of two transitions denoted as D0 and
D1 . The dependency relation is given as dep = {(A, D1 ), (B, D0 )} and the can-enable relation
A B D
is ce = {(B, D1 )}. Now assuming that the path s0 − → s1 −→ s2 −→ 0
s3 is explored first, then the
enabled transitions en and persistent sets ps for the visited states would be:
So only the additional transition sequence A,C0 , B,C1 , starting from the initial state s0 , would be
explored. And the transition sequence C0 , B,C1 , A, which leads to an error state from s0 , would
be missed.
1 int x = 0; 5 assert ( x == 8 thread B { 12 thread D {
2 event e ; 0) ; 9 notify e ; 13 wait e ;
3 6 } 10 } 14 x = 1;
4 thread A { 7 11 15 }
• If t is disabled in s, then all transitions of one necessary enabling set nes(s,t) are also
in T (t ∈/ en(s) =⇒ ∃nes(s,t) : nes(s,t) ⊆ T ).
According to [God96] this definition resembles strong stubborn sets as defined in [Val89].
And every stubborn set conforming Definition 12 is also a persistent set.
A concrete generic algorithm that computes stubborn sets according to Definition 12 is shown
in Algorithm 4. It is generic in the sense that it only depends on a valid dependency relation
dep between transitions and a way to compute necessary enabling transitions. The function
nes can be defined based on a valid can-enable relation ce, which has been already defined as:
(t1 ,t2 ) ∈ ce iff t1 can enable t2 . Two different implementations nes1 and nes2 are given in the
following. They receive the current state s and a transition t that is disabled in s as arguments.
The first implementation simply returns all transition that can directly enable t. Clearly one of
them must be executed in any trace starting from s that leads to the execution of t.
←
nes1 (s,t) = ce[t] = {(a, b) | (b, a) ∈ ce}[t] = {b | (b, a) ∈ ce}
The second implementation exploits the fact that for all states s only a single transition is active
for each thread, denoted as next(s,thread). This transition can either be enabled or disabled in
2.6. Partial Order Reduction 25
state s. It will be the next transition executed for the thread either in s or any of its successor
states. Let tn = next(s,thread(t)) be the currently active transition for the thread that contains
the transition t. So if t = tn , then tn must be executed before t in every trace starting from s.
Consequently {tn } is a necessary enabling set for t in s in that case. Else t = tn , so t is the
transition that would be executed next on its containing thread, all transitions are returned that
can enable t.
{tn } if t = tn
nes2 (s,t) =
nes1 (s,t) else
The stubborn set algorithm using the first implementation will be called STUB1 or simply
STUB. When the second implementation is used, it will be called STUB2 .
Discussion
Two different algorithms have been presented, the conflicting transitions algorithm and the
stubborn set algorithm. Both of them return persistent sets according to Definition 10. Both
algorithms take a state s and a transition t that is enabled in s as argument. The stubborn set al-
gorithm will always yield smaller or equal persistent sets compared to the conflicting transitions
algorithm.
The stubborn set algorithm is available in two configuration that differ in the way necessary
enabling transitions are computed. The first one uses the definition nes1 , the second one uses
nes2 . They will be referred to as stub1 and stub2 respectively. Neither of them is always
better than the other one, in the sense that it will always return a smaller persistent set for the
same state s and initial transition t. For example consider the example program in Listing 2.7.
It consists of four threads A, B, C and D. The threads A, B and C consist only of a single
transition denoted as A0 , B0 and C0 respectively. The thread D consists of three transitions D0 ,
D1 and D2 separated by the context switches in the lines {21, 22}. Let s0 be the initial state,
thus en(s0 ) = {A0 , B0 ,C0 , D0 }. The dependency and can-enable relations are defined as:
1 /* 6 event e2 ; 14 } 22 wait e2 ;
2 Unsafe program , 7 15 23 assert x == 1;
D -> C -> D 8 thread A { 16 thread C { 24 }
-> B -> D 9 x = 1; 17 notify e1 ; 25
will fail . 10 } 18 } 26 main {
3 */ 11 19 27 start ;
4 int x = 0; 12 thread B { 20 thread D { 28 }
5 event e1 ; 13 notify e2 ; 21 wait e1 ;
Listing 2.7: Example program for comparing the stubborn set algorithms
Choosing the enabled transition A0 in s0 , the first algorithm will yield stub1 (s0 , A0 ) = en(s0 )
whereas the second algorithm will yield a smaller persistent set stub2 (s0 , A0 ) = {A0 ,C0 , D0 }. On
the other hand let s1 be the state reached by executing B0 from s0 , thus en(s1 ) = {A0 ,C0 , D0 }.
Choosing the enabled transition A0 in s1 , the first algorithm will yield stub1 (s1 , A0 ) = {A0 }
but the second algorithm will yield a larger persistent set in this case namely stub2 (s1 , A0 ) =
{A0 ,C0 , D0 }. So it might be useful to compute both persistent sets stub1 (s,t) and stub2 (s,t) and
choose the smaller one of them.
All of the above algorithms compute a persistent set by starting with a transition t ∈ en(s).
Thus the resulting persistent set for each state depends on the initially chosen transition t. Con-
sequently one might consider to compute all possible persistent sets in a state s and choose the
smallest one of them. This results in locally optimal persistent sets with respect to the cho-
sen algorithm but not necessarily in globally optimal decisions. Always choosing the smallest
persistent set in each reached state does not necessarily lead to the exploration of the smallest
possible number of transitions and states, thus it only is a heuristic. Therefore it might even be
useful to select a locally non minimal persistent set to achieve a globally maximum reduction
[GP93; God96]. It has been shown in [Pel93] that calculating an ample set, which is in princi-
ple similar to a persistent set, for each state that leads to a minimal reduced state graph AR is
NP-Hard. Thus the problem of selecting an optimal persistent set in each state seems inherently
complex.
Definitions
Every transition t executed from a state s can be associated with a set of visible effects. Visi-
ble effects are informations that are relevant to decide whether two transitions are dependent.
They contain for example identifiers of variables (or memory locations) which may be shared
with other threads and have been accessed, together with the access mode (e.g. read/write).
They also contain thread/transition identifiers which have been disabled and events that have
been immediately notified. Basically enough information is tracked during the execution of a
transition such that given two effect sets e1 and e2 , it can be decided whether e1 and e2 are
dependent, according to Definition 4 of transition dependencies. Basically enough information
is tracked during the execution of a transition such that given two effect sets e1 and e2 , the IVL
specific transition dependency relation, given in Section 8, can be (conservatively) decided be-
tween them. Given a set of visible effects e, the corresponding transition and its thread can be
2.6. Partial Order Reduction 27
Algorithm Summary The DPOR state space exploration algorithm is based on a DFS search,
thus it alternates between transition exploration and backtracking. The algorithm starts by ex-
t1 t2 tn
ploring an arbitrary path s0 = s1 → s2 → ...sn → sn+1 (transition sequence) from the initial state
7
s0 until a terminal state is reached. The visible effects of every executed transition are tracked.
Whenever a state is backtracked from the search path, the effects of the last transition t will be
compared with all the effects of all relevant previous transitions ti . If a dependency is detected,
backtracking points are added to the state si , from which ti has been explored. Basically it
means that si is associated with additional transitions si .working that need to be explored from
7A state where no transition is enabled.
28 2. Preliminaries
si . Backtracking stops once a state s is reached which contains unexplored backtracking points.
An arbitrary transition s.working is chosen and explored until a terminal state is reached. The
search continues until the initial state is backtracked (or an error is detected), which means the
complete relevant state space has been explored.
returns when all transitions in s.working, and their successors, by further recursive calls, have
been explored and backtracked. The dependency analysis that is called for each backtracked
transitions might have computed backtrack points for the current state s, so s.working can again
be non empty. Eventually the while loop will terminate, since only a finite number of transi-
tions is enabled in each state s and every recursive call to exploreTransitions explores at least
one of them. Once they are all explored, so s.done = en(s), no more transitions can be added
to s.working.
The exploreTransitions function takes two arguments, a state s and a set of transitions T ,
which for this algorithm is always s.working. First t is added to the s.done set and removed
from the s.working set. This avoids re-exploration of t from s. Then the successor state n of
s when executing t, together with the visible effects e observed during the execution of t, are
obtained (lines {14, 15}). The search path P is extended accordingly with the effects e of t
and successor state n. The recursive call to explore continues with the last state of P, which
has been setup to be n. Once the explore function returns, all paths from the next state n have
been explored and backtracked. Finally the transition t is backtracked from s. This includes two
actions. First the extensions to the search path P before the recursive call to explore are reversed
in Line 18. So basically the search path is restored to the state it had before the push in line
Line 16. It allows to explore the next transition in T in the next loop iteration. And second the
backtrack dependency analysis is applied for s and e, by calling the normalBacktrackAnalysis
function. Basically the effects e from s are compared with all relevant previous effects currently
on the search path, which are the effects of those transitions leading to the state s from the initial
state s0 .
Remark. The backtrack analysis is triggered after the recursive explore call in line Line 17.
This is just a design decision. It could also happen before the explore call. Doing so would im-
mediately compute backtrack points once a transition is explored. The current implementation
on the other hand delays the computation of backtrack points until the transition is backtracked
from the search path. Doing so seems more reasonable, since a path can be fully explored until
a terminal state without computing backtrack points. This allows to detect error states slightly
faster.
The function normalBacktrackAnalysis is called for backtracking a transition t, which is rep-
resented by its visible effects e, from state s. All transitions that have been disabled during the
execution of t are added as backtrack points T to s. That means all transition in T that have
not yet been explored from s (are not in s.done) are added to s.working. Then the effects e of t
from s are compared with all relevant effects e p of all transitions t p and their originating states
s p currently in the search path. All transitions t p which have been executed in the same delta
cycle as t are relevant. They are obtained by using the function transitionsOfLastDeltaCycle in
Line 24. It returns a possibly empty set of pairs (s p = si , e p = ei ) for i ∈ {l..n} such that all si
are states where the simulation is in the evaluation phase and the immediate predecessor sl−1
is a state where the simulation is in the notification phase 8 . Whenever a dependency between
two effect sets e and e p is detected, as defined in Section 8, some backtrack points are added.
If thread(e) is enabled in s p than a single backtrack point is added, namely the next transition
of thread(e) that will be executed in s p . Else the dynamic analysis was unable to infer a non
trivial persistent set for s p and all enabled transitions in s p are added as backtrack points.
The DPOR algorithm presented in this section performs a stateless exploration. In Chapter
4 it will be further extended to perform a stateful exploration of first acyclic and then arbitrary
8 Thustl−1 is a transition that executes a delta- or timed notification such that the successor sl is a state in the
evaluation phase
30 2. Preliminaries
Algorithm 6: hbrBacktrackAnalysis(s, e)
Input: State and visible effects of the last transition
Output: Backtrack analysis with using a happens before relation
1 procedure hbrBacktrackAnalysis(s, e) is
2 for s p , e p ∈ transitionsO f LastDeltaCycle(P) do
3 if areDependent(e p , e) then
4 E ← {thread(t) | t ∈ enabled(s p )}
5 if thread(e) ∈ E then
6 T ← {next(s p , thread(e))}
7 else if ∃e j after e p with e j P thread(e) and thread(e j ) ∈ E then
8 T ← next(s p , thread(e j ))
9 else
10 T ← enabled(s p )
11 addBacktrackPoints(s p , T)
finite state spaces. The algorithm is separated into multiple functions, so it can be more easily
extended. It simplifies the identification of common parts between the different version of the
algorithm.
Example Exploration This section presents a small example program that shall illustrate the
above stateless DPOR exploration. The program is available in Listing 2.8. It consists of three
threads A,B and C. Each of them has a single transition, thus they will also be simply referred
to as A,B and C. Its state space is finite and acyclic, thus all assertion violations will be found
by the above DPOR algorithm. The program is unsafe, the transition sequence B,C will violate
the assertion in Line 17.
9 The extended backtrack analysis presented here simply assumes that the current happens before relation is
available in Line 7. An actual implementation has to compute and maintain such a relation.
2.6. Partial Order Reduction 31
1 int a = 0; 8 14 20
2 int b = 0; 9 thread B { 15 thread C { 21 main {
3 10 if ( a == 0) { 16 if ( b > 0) { 22 start ;
4 thread A { 11 b = 1; 17 assert 23 }
5 a = 1; false ;
6 b = 0; 12 } 18 }
7 } 13 } 19 }
s0
A B
s1 s4
B A C
s2 s5 s7
C C
s3 s6
Figure 2.5.: Example scheduling that may be performed by the stateless DPOR algorithm for the program
in Listing 2.8
Table 2.1.: Relevant informations when applying the DPOR algorithm to the program in Listing 2.8
step search path working done
0 s0 { s0 : {A}} { s0 : 0}
/
A
1 s0 −
→ s1 { s0 : 0,
/ s1 : {B}} { s0 : {A}, s1 : 0}
/
A B
2 s0 −
→ s1 −
→ s2 { s0 : 0, / s2 : {C}}
/ s1 : 0, { s0 : {A}, s1 : {B}, s2 : 0}
/
A B C
3 s0 −
→ s1 −
→ s2 −
→ s3 { s0 : 0,
/ s1 : 0,
/ s2 : 0,
/ s3 : 0}
/ { s0 : {A}, s1 : {B}, s2 : {C},
s3 : 0}
/
A B
4 s0 −
→ s1 −
→ s2 { s0 : {C}, s1 : 0,
/ s2 : 0}
/ { s0 : {A}, s1 : {B}, s2 : {C}}
A
5 s0 −
→ s1 { s0 : {B,C}, s1 : 0}
/ { s0 : {A}, s1 : {B}}
6 s0 { s0 : {B,C}} { s0 : {A}}
B
7 s0 −
→ s4 { s0 : {C}, s4 : {A}} { s0 : {A, B}, s4 : 0}
/
B A
8 s0 −
→ s4 −
→ s5 { s0 : {C}, s4 : 0,
/ s5 : {C}} { s0 : {A, B}, s4 : {A}, s5 : 0}
/
B A C
9 s0 −
→ s4 −
→ s5 −
→ s6 { s0 : {C}, s4 : 0,
/ s5 : 0,
/ s6 : 0}
/ { s0 : {A, B}, s4 : {A}, s5 : {C},
s6 : 0}
/
B A
10 s0 −
→ s4 −
→ s5 { s0 : {C}, s4 : {C}, s5 : 0}
/ { s0 : {A, B}, s4 : {A}, s5 : {C}}
B
11 s0 −
→ s4 { s0 : {C}, s4 : {C}} { s0 : {A, B}, s4 : {A}}
32 2. Preliminaries
An example scheduling, that the DPOR algorithm may perform, is shown in Figure 2.5. The
states are named in the order they are visited, so s0 is visited first and s7 last. Some relevant
information that the DPOR algorithm tracks during the execution of the program is available in
Table 2.1. It shows the search path together with the working and done sets for each state on the
search path. Already explored transitions are stored in the done set and transitions that have been
identified during the backtracking are stored in the working set for each state. Every row in the
table shows a snapshot of these informations. The step column is used to identify the snapshots
during the following description. Initially all transitions are enabled, thus en(s0 ) = {A, B,C}.
Whenever a transition is executed it will be disabled in the successor state.
Initially in step 0, the search path contains only the initial state s0 , all transitions A,B,C
are enabled in s0 . The transition A has been selected to be explored first, hence s0 .working =
{A}. Doing so will move the system into step 1. The sets s0 .working and s0 .done have been
updated to 0/ and {A} respectively. The last state on the search path is s1 , which is the successor
of s0 when executing transition A. The transition B is selected to be explored from s1 , thus
s1 .working = {B}. Doing so will reach the state s2 (step 2). The done and working sets of s1 are
update accordingly. Next the last enabled transition C is selected to be explored from s2 . This
will reach the terminal state s3 (step 3).
Since the last state of the search path s3 is a terminal state, the backtrack analysis will be
triggered. The last transition C is checked for dependencies with the previous transitions. It
is independent with B from s1 , since Line 11 has not been executed on this path, but has a
read/write dependency with A from s0 . The transition C is enabled in s0 and has not been
explored from s0 so far, thus s0 .working is updated to {C}. The last state s3 is removed from the
path. Now (step 4) s2 is the last state and B the last transition. Since s2 .working = 0/ backtracking
continues. So B is compared with all previous transitions. A read/write dependency between B
and A from s0 is detected. Hence B is added to s0 .working. The last state s2 is removed from
the path. Now (step 5) s1 is the last transition. Backtracking continues, since s1 .working is also
empty. Since no transitions have been executed before A, and A does not disable any transitions
itself, no further backtrack points are computed. So s1 is removed from the path and s0 becomes
the last state (step 6).
Its working set is not empty, so backtracking stops and the normal exploration continues.
Now B is selected to be explored resulting in the state s4 (step 7). Then A is selected reaching
s5 (step 8) and lastly C is executed reaching the terminal state s6 (step 9). Since the last state is
a terminal state, backtracking is applied. The last transition C is compared with the transitions
A from s4 and B from s0 . It turns out that C is dependent with both transitions. Both working
sets, of s0 and s4 , are updated to contain C. The working set of s5 is not updated, since C
does not disable any other transition. Now s6 is removed and s5 becomes the last state (step
10). Backtracking continues since s5 .working is empty. The last transition A is detected to be
dependent with B from s0 . Since A ∈ s0 .done, the working set of s0 is not updated. Next s5
is removed and s4 becomes the last state (step 11). Its working set contains only C, thus C is
explored from s4 . Doing so will violate the assertion in Line 17 and the search terminates10 .
10 Otherwise it would terminate once the initial state has been backtracked, which means that the complete state
space has been explored.
3. Static Partial Order Reduction in Stateful
Model Checking
POR is a particularly effective technique to alleviate the state explosion problem by limiting
the exploration of redundant transition interleavings. POR works by selecting only a subset
of enabled transitions in each step, which are sufficient to prove the desired properties. The
other transitions are temporarily ignored, since they are non-interfering with those that are se-
lected. A stateful search on the other hand avoids re-exploration of already visited states. A
naive combination of both techniques can lead to the point, where a relevant transition is per-
manently ignored due to a cycle in the reduced state space. This situation is commonly referred
to as (transition/action) ignoring problem. It has been first identified by [Val89]. The ignoring
problem needs to be solved in order to preserve properties more elaborate than deadlocks. The
focus of this thesis is on the verification of safety properties specified in the form of assertions,
thus the ignoring problem has to be considered. The solution is to incorporate a so called cycle
proviso to prevent transition ignoring.
The rest of this chapter is organized as follows: First sufficient conditions will be presented
such that a partial order reduced state space exploration preserves all deadlocks and safety
properties (assertion violations). Then based on these conditions, concrete implementations of
stateful static partial order reduced state space explorations are presented.
3.1.1. Deadlocks
In order to preserve all deadlocks in AR it is sufficient that the following two conditions hold for
every state s in the reduced state space (s ∈ SR ) [God96]:
C0 r(s) = 0/ iff en(s) = 0/
1A trivial sufficient condition would be that r(s) = enabled(s) for each state s ∈ SR . Since AR = AG in this case,
all properties of AG are clearly preserved. But no reduction has been achieved at all. So the idea is to use a
reduction function that will preserve all properties of interest while yielding a good state space reduction.
A A
B
s0 B s1
Figure 3.1.: Abstract example state space demonstrating the ignoring problem
By Definition 3 of reduced state spaces, the initial state s0 is always in AR . The idea is to
show that at least one equivalent trace w ∈ [w]s0 is explored in AR . By Definition 6 of equivalent
traces, both of them will lead to the deadlock sd in AG . The complete proof is available in the
Appendix in Section A.2.
1 event eA ; 10 } 19 thread D {
2 event eB ; 11 20 delay 0;
3 12 thread B { 21 assert false ;
4 thread A { 13 while ( true ) { 22 }
5 delay 0; 14 wait eB ; 23
6 while ( true ) { 15 notify eA ; 24 main {
7 notify eB ; 16 } 25 start ;
8 wait eA ; 17 } 26 }
9 } 18
s0 s1 s2 s3 s4 s5 s6 s7
A1 B1 D1 tN A2 B2 A3
Figure 3.2.: Possible reduced state space for the program in Listing 3.1 satisfying conditions C0 and C1 .
The following transitions are enabled and selected for each state:
Thus clearly the reduced state space defined by the reduction function r satisfies the conditions
C0 and C1 . The transition D2 is enabled in s4 but never explored from s4 or any state reachable
from it. Consequently the assertion violation in Line 21 is missed.
Remark. There also exists a reduced state space satisfying C0 and C1 and detecting the assertion
violation. But in general C0 and C1 are not sufficient to enforce this.
C2 For every state s in AR , if t ∈ en(s) then there is a state s reachable from s in AR such that
t ∈ r(s ).
Basically it states that any enabled transition is eventually explored in the reduced state space.
It is a sufficient condition to prevent the ignoring problem.
Conditions C0 , C1 and C2 together are sufficient to preserve safety properties, e.g. specified
in form the of assertions, from AG in AR . In fact the condition C0 is no longer necessary (it was
36 3. Static Partial Order Reduction
B2 A3
D2 s8 s9 s10
s0 s1 s2 s3 s4 s5 s6 s7
A1 B1 D1 tN A2 B2 A3
Figure 3.3.: Possible reduced state space for the program in Listing 3.1 satisfying conditions C1 and C2 .
for deadlocks though). The reason is that C2 is already strictly stronger and thus implies C0 ,
as stated by the following Lemma 3.1.2. This can be easily shown by a direct proof, which is
available in the Appendix in Section A.6. In the following condition C0 will still sometimes be
explicitly considered even though it is actually unnecessary.
Thus the conditions C1 and C2 are already sufficient to preserve all assertion violations from
AG in AR . The correctness of this proposition is established by the following theorem.
Theorem 3.1.3
Let AR be a reduced state space where the reduction function r satisfies the conditions C1
and C2 as defined in this section. Let w be a trace in AG that leads to an error state from the
initial state s0 . Then there exists a trace wr in AR from s0 such that w ∈ Pre f ([wr ]s0 ).
The idea is to show that for each trace w in AG from s at least one trace w is explored in AR
from s such that w is a prefix of w , denoted as w ∈ Pre f ([w ]s ). The function Pre f takes a set of
traces W and returns the set of all prefixes defined as Pre f (W ) = {w p | w p is a prefix of any w ∈
W }. For example let w = ACB be a trace explored from a state s where the transitions B and C
are independent, then the equivalent traces of w are defined as [w ]s = {ACB, ABC}. Thus the
set of prefixes of equivalent traces of w is defined as:
Example 2. Consider again the example program from the previous section demonstrating the
ignoring problem in the context of SystemC. The (relevant) transition D2 is enabled in the states
{s4 , s5 = s7 , s6 } but never explored. Condition C2 ensures that D2 ∈ r(s j ) for some j ∈ {5..7}.
Lets assume that D2 ∈ r(s5 ). The modified explored state space is shown in Figure 3.3. The trace
w = A2 B2 D2 leads to an error state in AG from s4 (the trace part from s0 to s4 is uninteresting for
this example). The trace w = A2 D2 B2 is in AR and w ∈ Pre f ([w ]s4 ), since D2 is independent
with B2 . Similarly D2 ∈ Pre f ([A2 D2 ]s4 ) and A2 B2 A3 B2 D2 ∈ Pre f ([A2 D2 B2 A3 B2 ]s4 ). In the
same way there exists a trace w in AR from state s for every trace w in AG such that w ∈
Pre f ([w ]s ).
A stronger condition C2S that, in combination with C1 , implies C2 but might be easier to
implement is (adapted from [EP10]):
C2S For every state s in AR there exists a reachable state s from s in AR , such that s is fully
expanded, i.e. r(s ) = en(s ).
3.2. Static Partial Order Reduced Exploration 37
This condition states it explicitly that all states s ∈ SR that reach a fully expanded state s are
safe from the ignoring problem. For IVL programs it directly implies that cycles of states that
span multiple delta cycles do not suffer from the ignoring problem, since a delta cycle can only
be left through a state s where only a single designated transition tN , namely the notification
phase transition, is enabled. Thus s is fully expanded. It follows immediately that every state on
the cycle with s can reach a fully expanded state. Consequently, the ignoring problem is only
limited to single delta cycles in the context of SystemC.
The correctness of the C2S condition is established by the following theorem. A proof is
available in the Appendix in Section A.6.
Theorem 3.1.4
The condition C2 is implied by the condition C1 and C2S .
thus it clearly satisfies the conditions C0 and C1 for all reachable states s. The function persis-
tentSet uses one of the algorithms presented in Section 2.6.2 to compute a persistent set in s
starting with an arbitrary enabled transition t in s.
3 procedure explore(s) is
4 if s ∈
/ H then
5 H.add(s)
6 for t ∈ selectTransitions(s) do
7 n ← succ(s,t)
8 explore(n)
The flag s.sa f e means that a fully expanded state s is reachable from s. Thus a safe state
clearly satisfies the sufficient cycle proviso C2S which implies the weaker proviso C2 . Whenever
a fully expanded state s is reached, all states that can reach s are necessarily sa f e too. The
states currently on the search stack represent a path from the initial state s0 to s , thus all of them
can be marked safe.
The flag s.un f inished means that s potentially lies on a cycle of states. A state s is marked
unfinished, if s has already been explored, s is not already marked safe and a state s is about to
be explored such that s = s . If a state s does not lie on a cycle it will eventually reach a terminal
state s . Since s is fully expanded, s will be marked sa f e. This leads to the point that every
state is either safe when backtracked or reaches an unfinished state.
Every state that is marked unfinished and not already marked safe can potentially ignore a
relevant transition, so it will be refined when backtracked. The following algorithms simply
fully expand a state during refinement. Thus ultimately every explored state can reach a fully
expanded state. Consequently the cycle proviso C2S is satisfied which implies the weaker proviso
C2 .
Incorporating the cycle proviso into the DPE algorithm, results in the exploration of a reduced
state space, which is induced by the following reduction function:
enabled(s) if ¬s.sa f e ∧ s.un f inished
r(s) =
selectTransitions(s) else
This reduction function satisfies the conditions C0 , C1 and C2 . But it only is a declarative
solution, the flags s.sa f e and s.un f inished, as described above, still need to be computed some-
how. The flags for a state s are naturally not available before s is explored. But to explore s,
by using the above reduction function r(s), the flags are required. The solution to break up
this circular dependency is to separate the exploration of transitions into multiple steps. Since
T = selectTransitions(s) ⊆ enabled(s), so the transitions in T would be explored anyway, first
all transitions in T are explored and afterwards s is refined if necessary. The following section
shows such an implementation.
Remark. As already mentioned, the proviso C2S has been introduced in [EP10]. The authors
already provide a description on how to integrate the proviso in a DFS. But they assume that it
can be decided for states that have not yet been explored whether they are safe or not. Compared
to it, this section provides an explicit, instead of a declarative, method to satisfy the cycle
proviso in a DFS.
3.2. Static Partial Order Reduced Exploration 39
4 procedure explore() is
5 s ← stack.top()
6 if s ∈ H then
7 v ← H[s]
8 if v.safe then
9 stack.markAllSafe()
10 else
11 v.unfinished ← True
12 else
13 H.add(s)
14 s.safe ← False
15 s.unfinished ← False
16 if r(s) = en(s) then
17 stack.markAllSafe()
18 T ← selectTransitions(s)
19 exploreTransitions(s, T)
20 if ¬ s.safe ∧ s.unfinished then
21 stack.markAllSafe()
22 exploreTransitions(s, en(s) \ T )
23 procedure exploreTransitions(s, T) is
24 for t ∈ T do
25 n ← succ(s, t)
26 stack.push(n)
27 explore()
28 stack.pop()
The explore procedure considers the last state s on the current search stack. If s has already
been explored, then the already visited state v ∈ H such that v = s is retrieved. If v is safe, then
all states in the search stack must also be safe, since they represent a path from s0 to s = v. The
markAllSafe procedure marks all of them to be safe. Otherwise v is marked as unfinished. Thus
v has eventually to be refined when backtracked. If s has not already been explored, then it will
be added to the cache H and the flags safe and unfinished are initialized to be False. Next it will
be checked whether the selected subset of transitions, due to POR, equals the set of all enabled
transitions. If so, s is fully expanded and can be marked safe. Thus all states that can reach s
can also be marked safe. This is done by calling the markAllSafe function of the search stack
in Line 17. It marks all states currently on the stack as safe. Since s is currently the top of the
stack, it will be marked too. Next a subset of enabled transitions T of s is selected in Line 18,
as defined for the DPE algorithm in Section 3.2.1. All transitions in T are recursively explored
by calling exploreTransitions in Line 19. Once the procedure returns, this state s is about to be
backtracked. So it is checked in Line 20, whether s can potentially ignore a relevant transition
and thus needs refinement. This is the case if s does not necessarily reach a fully expanded state
(¬s.sa f e) and may potentially be on a cycle of states (s.un f inished). In this case s will be fully
expanded. Therefore s necessarily is safe and all states currently in the stack too, since they lead
to s, thus they are all marked safe in Line 21. Consequently, all enabled transitions which have
not been explored so far, are recursively explored in this case in Line 22. Finally, and similarly
to the previous algorithms presented so far, the cache H will be notified that s is backtracked.
A backtracked state is no longer (or immediately will be removed as in this algorithm) on the
search stack, thus it cannot form a cycle with any unexplored state currently on the search stack,
which makes it a good candidate to free some cache memory, if required, without affecting the
correctness of the exploration algorithm as already discussed in Section 2.5.
Remark. The algorithm presented in this section preserves assertion violations in general, inde-
pendent of the employed algorithm to compute persistent sets. However, under certain condi-
tions the ignoring problem can already be implicitly solved. More information on this topic is
available in the Appendix in Section A.9.
4. Dynamic Partial Order Reduction in
Stateful Model Checking
This chapter presents a stateful DPOR algorithm that is applicable in the context of SystemC.
Preliminaries regarding DPOR are already presented in Section 2.6.3, including basic defini-
tions and a basic stateless algorithm based on [FG05] and [KGG08]. The algorithm is sound,
it will preserve all assertion violations, if the state space is finite and acyclic. However, a naive
stateful extension is potentially unsound as relevant transition dependencies may be missed dur-
ing backtracking. This situation will be referred to as the missing dependency problem. It can
arise in both acyclic and cyclic state spaces. The goal of this section is to extend the basic al-
gorithm to support cyclic state spaces, resulting in the SDPOR (Stateful DPOR) algorithm. To
achieve this goal, a step-wise extension is employed:
1. The first section introduces the missing dependency problem in acyclic state spaces. Then
the stateless algorithm will be extended to a stateful exploration of acyclic finite state
spaces, called A-SDPOR (Acyclic SDPOR), in Section 4.2. Thus it is merely an optimiza-
tion that avoids multiple explorations of the same state. The extensions are in principle
similar to those proposed in [Yan+08]. However they have to be adapted to the SystemC
context.
2. The A-SDPOR algorithm is unsound when applied to cyclic state spaces, since it will no
longer compute persistent sets. The problem, which is a variant of the missing dependency
problem, will be described in Section 4.3. A novel extension of the A-SDPOR that solves
the missing dependency problem in cyclic state spaces is proposed in Section 4.3. The
resulting algorithm is denoted C-SDPOR (Cyclic SDPOR).
3. In general the ignoring problem has to be solved in order to preserve safety properties1
in cyclic state spaces. The C-SDPOR algorithm, in its standard form, already implicitly
solves the ignoring problem in the context of SystemC. Thus an explicit cycle proviso is
not required for the standard C-SDPOR algorithm. However all DPOR variants can fall
back to static persistent sets, whenever the inference of non trivial persistent sets fails
dynamically. Doing so will make the C-SDPOR susceptible to the ignoring problem. For
this reason the C-SDPOR algorithm is combined with the cycle proviso C2 , as already
defined in Section 3.1.3 and further discussed in Section 3.2.2, in Section 4.4 to solve the
ignoring problem in general, independent of the method used to compute persistent sets2 .
The resulting algorithm is denoted simply as SDPOR, since it is the final version.
1 Andalso liveness properties, or in general all properties more elaborate than deadlocks.
2 Combining the DPOR algorithm with static persistent sets in general requires the integration of a cycle proviso.
The DPOR algorithm itself could use a more sophisticated dynamic analysis to infer persistent sets, which
could yield a smaller number of trivial persistent sets (fully expanded state) and thus require an explicit cycle
proviso.
1 int a = 0; 8 14 20
2 int b = 0; 9 thread B { 15 thread C { 21 main {
3 10 if ( a == 0) { 16 if ( b > 0) { 22 start ;
4 thread A { 11 b = 1; 17 assert 23 }
5 a = 1; false ;
6 b = 0; 12 } 18 }
7 } 13 } 19 }
Listing 4.1: Example program that appeared in the preliminaries as Listing 2.8 to demonstrate the basic
DPOR algorithm
s0 s0
A B A B
s1 s4 s1 s4
B A C C
B A
s2 s5 s7 s2 / s5 s7
C C C
s3 s6 s3 / s6
Figure 4.1.: Example scheduling that may be Figure 4.2.: Relevant part of the complete state
performed by the stateless DPOR space for the exploration of the
algorithm for the program in List- program Listing 4.1 following the
ing 4.1 execution order as shown in Figure
4.1
B A
return false up to this point). The current search path is s0 − → s4 −→ s5 . It is detected that
s5 = s2 ∈ V and backtracking starts with s5 as the last state. Consequently, the transition C,
which would have been executed from s5 and has been executed from s2 , is not considered by
the backtrack analysis. Thus the dependency of C with A from s4 is missed and C is not added to
the s4 .working set, which in turn does not lead to the exploration of C from s4 and the assertion
violation in Line 17 of the program is missed.
A-SDPOR Algorithm
The A-SDPOR algorithm is an extension to the basic stateless Algorithm 5. The modifications
are shown in Algorithm 9. Functions, which are not shown there, are inherited from the basic
algorithm without changes. Changes in existing functions are underlined for convenience. The
A-SDPOR algorithm keeps a cache H to avoid re-exploration of already visited states and a
directed graph GV to keep a record of the visible effects for every executed transition. Whenever
a transition t is executed from state s leading to the successor state s , an edge (id(s), id(s )) is
added to GV , where id(s) is some unique identifier for s. The edge is associated with the visible
effects e, that have been tracked during the execution of t from s Line 23.
The states itself are not stored in GV but only some unique identifier (e.g. a unique number or
the memory address of the state, depending on the actual implementation). Storing the complete
state would result in a significant memory overhead. Only the visible effects are stored which
are collected during the execution of the corresponding transition. These are normally relatively
small, since they only contain simple identifiers, e.g. memory locations that have been accessed
or events that have been immediately notified.
In the following the transition itself will also be displayed alongside the source and target state
for convenience. So edges will be written as (s,t, s ) instead of (s, s ) even though the transition t
can be unambigously obtained, given the source and target states (s, s ). The notation E(s,t, s )
returns the visible effects, which were observed when executing transition t from s reaching
state s . The corresponding transition and its thread can be obtained from the visible effects e
by transition(e) and thread(e) respectively.
Whenever the currently explored state s is matched with an already visited state v (Line 7),
an extended backtrack analysis will be initiated and the exploration of s stops (so afterwards
a normal backtrack analysis will be applied). The extended analysis will lookup v in GV and
find all visible effects of transition that can be executed from v or any of its successor states.
44 4. Dynamic Partial Order Reduction
For example if GV =[(s0 , A, s1 ), (s1 , B, s2 ), (s2 ,C, s3 )] and v = s1 the call in Line 29 would col-
lect the reachable effects {E(s1 , B, s2 ), E(s2 ,C, s3 )}. The visible transition effects can than be
used to compute dependencies with transitions (and their collected visible effects during their
execution) of the current search path.
Example
For example consider again the example program in Listing 4.1. Due to the missing dependency
problem, which has been introduced in the last section, a naive stateful extension could miss the
assertion violation in Line 17. Lets assume that this acyclic SDPOR algorithm has reached state
A B C B A
s5 , by first executing s0 −
→ s1 −→ s2 − → s3 , then backtracking to s0 and executing s0 − → s4 −→ s5 ,
which is now the current search path P. Up to this point it behaved exactly as the stateless
DPOR algorithm, since s5 = s2 is the first state that has already been visited. Thus the current
state s = s5 and the already visited state v = s2 . The visible effects graph GV currently contains
the following edges: [(s0 , A, s1 ), (s1 , B, s2 ), (s2 ,C, s3 ), (s0 , B, s4 ), (s4 , A, s5 )] So for this example
the extended backtrack analysis would start at v = s2 and collect all reachable edges, which are
in the same delta cycle, since the dependency analysis does not proceed across delta cycles in
the DPOR algorithm. In this case only a single edge will be returned, namely (s2 ,C, s3 ). The
effects of all collected edges are used to compute dependencies with transitions currently in the
search path P. Thus the dependency of C from s2 with A from s4 is detected and C is added to
the s4 .working set, which in turn will eventually lead to the exploration of C from s4 and the
assertion violation in Line 17 of the program is detected correctly.
Remark. The extended backtrack starts with an already visited state v and collects a set E of all
reachable visible effects. All effects e ∈ E are then checked for dependencies with transitions
4.3. Supporting Finite Cyclic State Spaces 45
(actually their visible effects) on the current search path P. The order in which the e ∈ E are
considered does not matter. The result will be the same.
Notes on Correctness
Assuming that the DPOR algorithm explores persistent sets for each state s, i.e. s.done is a
persistent set for each state s when the search is completed, it follows immediately that the A-
SDPOR algorithm explores persistent sets too when applied to a finite acyclic state space. If no
state s is matched with an already visited state v, then DPOR and A-SDPOR behave completely
identically. Otherwise, a state s is matched with an already visited state v. The state v has
already been backtracked and thus is no longer on the search path P, else the state space would
be cyclic which it is not by assumption. Consequently, all relevant transitions from v have
already been explored and their visible effects recorded. Since v is completely equal with s the
effects generated from v are exactly the same than those that would be generated from s. And
since the state space is finite and acyclic by assumption, they are exactly the same a stateless
DPOR would yield. Consequently, if the basic stateless DPOR algorithm is correct (in the sense
that it preserves all deadlocks and assertion violations), than this stateful extension will also be.
The stateless algorithm may visit the same transition multiple times, since the same state may be
reached multiple times on different paths. The stateful extension on the other hand will explore
each state and each of its transitions at most once.
missed
s10 s11
B2 A3
e
C2 C2
s0 s1 s2 s3 s4 s5 s6 s8 s9
A1 B1 C1 tN A2 B2 C2 tN A3
s7
Figure 4.3.: Example state space that the A-SDPOR algorithm could explore for the program in Listing
4.2. The designated transition tN denotes the execution of a notification phase as defined
in Section 2.4.1. Thus they separate the delta cycles of the simulation. The dotted states
and transitions are relevant to detect the error state e, but they are missed by the A-SDPOR
algorithm in this exploration (and thus in general). Matched states are connected by dashed
lines with unfilled arrows, where the target is an already explored state currently in the cache.
An IVL program that demonstrates this missing dependency problem is shown in Listing 4.2.
It consists of three threads and they in turn consist of multiple transitions, denoted as A1 , A2 and
so on, separated by context switch statements (all wait 0 statements). The program is unsafe,
as the transition sequence A1 B1C1tN A2 B2C2tN C2 will lead to an error state. The designated tran-
sition tN denotes the execution of a notification phase, which is a SystemC specific simulation
detail, which transfers the simulation from one delta cycle to the next.
A possible state space exploration, which the A-SDPOR would perform, is available in Fig-
A B C
ure 4.3. The states are visited in the order they are numbered. So the path s0 −→
1
s1 −→
1
s2 −→
1
tN A2 B2 C2 tN A3
s3 −→ s4 −→ s5 −→ s6 −→ s7 − → s8 −→ s9 is explored first. It will be detected that s9 = s4 has
already been visited. Thus the A-SDPOR algorithm will initiate the extended backtrack anal-
ysis. Starting from v = s4 all (distinct) reachable effects will be collected, that are in the same
delta cycle as v = s4 , these are: [(s5 , B2 , s6 ), (s6 ,C2 , s7 )], and analyzed with the transitions ex-
ecuted in the last delta cycle of the current path P for dependencies, these are: [(s8 , A3 , s9 )].
Neither the transition B2 from s5 nor C2 from s6 writes the global c variable, thus there exist no
dependencies with A3 from s8 which asserts that the value of c is zero (thus reads c). Since no
dependencies are detected, no backtrack points are added. Even though there exists a feasible
A C B
path namely s8 −→ 3
s9 = s5 and then s5 −→
2
s10 −→
2
s11 such that C2 from s5 is read/write depen-
dent with A3 from s8 . So actually C2 should be added to s8 .working. But it does not happen
when the A-SDPOR algorithm is used. The problem is that C2 has not yet been explored from
s5 , so it is not considered during this dependency analysis. It will only be explored once back-
tracking reaches the state s5 3 . Consequently, C2 will not be explored from s8 and the assertion
violation in Line 8 is missed.
Remark. Executing C2 from s5 in this example will result in a write access to the global variable
c, thus a read/write dependency with A3 from s8 would be available. The execution of C2 from
s6 on the other hand resulted only in a write of the global variable b, thus it was not dependent
with A3 .
3 The dependency between B2 from s5 and C2 from s6 will be detected later on during normal backtracking. Since
C2 is enabled in s5 it will then be added to s5 .working. Once backtracking does reach the state s5 , C2 will be
explored from s5 . But at this point s8 has already been backtracked and is no longer available on the search
path.
4.3. Supporting Finite Cyclic State Spaces 47
Listing 4.2: Example program to demonstrate that the A-SDPOR algorithm is unsound for cyclic state
spaces
s4 s5 s6
B A
D D D
A B
s1 s2 s3
4 All of the last delta cycle, since the DPOR algorithm does not backtrack across delta cycles in the context of
SystemC, as already mentioned in Section 2.6.3.
4.4. Complete Stateful Exploration 49
A partial order reduced state space exploration of cyclic state spaces has to handle the ignoring
problem, as introduced in Section 3.1.2, in order to preserve more elaborate properties than
deadlocks. The C-SDPOR algorithm naturally solves the ignoring problem. Since the state
space is finite, every path w from every state s will either reach a terminal state or run into a
cycle. A terminal state by definition is fully expanded. By definition of the C-SDPOR algo-
rithm, for every cycle of states, at least one state is fully expanded. Thus from every state of the
cycle a fully expanded state is reachable. So in either case s will reach a fully expanded state.
Thus the cycle proviso C2S , which implies the weaker condition C2 , is satisfied by the C-SDPOR
algorithm.
It is left to show that the C-SDPOR algorithm satisfies condition C1 , which states that r(s) =
s.done is a persistent set when s is finally backtracked. In Section 30 it has been reasoned that
the A-SDPOR algorithm explores persistent sets for each state in a finite acyclic state space.
The idea has been to reduce it to the basic stateless DPOR algorithm. The C-SDPOR works
identically to the A-SDPOR algorithm for acyclic state spaces. So it is left to show that the C-
SDPOR algorithm explores persistent sets for each state in a finite cyclic state space. A formal
proof (or disprove by counterexample) of this property is beyond the scope of this thesis and
left for future work.
When C-SDPOR is configured to fall back to static persistent sets, denoted as combined C-
SDPOR, than it no longer does satisfy the condition C2 , so it suffers from the ignoring problem.
In fact the same example program shown in Listing 3.1 in Section 3.1.2 that demonstrates this
problem for SPOR will also produce the problem for the combined C-SDPOR. Thus it makes
sense to also further extend the C-SDPOR algorithm to explicitly handle the ignoring problem
independently of implementation details on how persistent sets are inferred.
This section extends the C-SDPOR algorithm such that the ignoring problem is solved in general
independent of the backtracking analysis or whether static persistent sets are used as fall back
whenever the inference of non trivial persistent sets fails dynamically. This extended algorithm
will simply be called SDPOR (stateful DPOR), as it is the final extension.
The SDPOR algorithm is completely equal to the C-SDPOR algorithm except for the ex-
plore procedure. The adapted explore function is available in Algorithm 11. The basic idea to
implement the DFS compatible cycle proviso C2S has already been described in Section 3.2.2,
when extending the deadlock preserving exploration (DPE) to an assertion violations preserv-
ing exploration (AVPE). The same principles also apply here. The idea is to mark states that
can reach a fully expanded state (safe) or potentially lie on a cycle (unfinished). Whenever a
state s is about to be backtracked that is unfinished and not safe, which means it can potentially
ignore a relevant transition, s will be fully expanded. Thus ultimately every explored state can
reach a fully expanded state. Consequently the cycle proviso C2S is satisfied which implies the
weaker proviso C2 . In the following the extended explore procedure, which implements the
cycle proviso, will be described in more details. Finally some notes about the correctness of
the SDPOR algorithm will be given. A complete implementation of SDPOR, that falls back to
static persistent sets, is available in the Appendix in Section A.8.
50 4. Dynamic Partial Order Reduction
14 else
15 H.add(s)
16 s.done ← {}
17 if enabled(s) = 0/ then
18 s.working ← {selectAny(enabled(s))}
19 while |s.working| > 0 do
20 exploreTransitions(s, s.working)
The explore function considers the last state s on the current search path P. If s has already been
explored, then the already visited state v ∈ H such that v = s is retrieved. If v is safe, then all
states in P must also be safe, since they represent a path from s0 to s = v. Else v is marked as
unfinished. Thus v has eventually to be refined when backtracked.
If s has not already been explored, then the SDPOR algorithm first works identical to the
C-SDPOR algorithm. The state s is added to the state cache. Then it will be initialized. During
initialization every newly reached state will be associated with a unset safe and unfinished flag.
Then identically to the C-SDPOR algorithm, an arbitrary enabled transition will be selected
and explored. This can lead to the detection of backtrack points due to dependencies between
the executed transitions. These are stored in the working set of s. The loop will iterate until
no further backtrack points are computed. The C-SDPOR algorithm would be finished at this
point. The SDPOR algorithm checks whether the current state s has to be refined to circumvent
the ignoring problem.
If all enabled transitions have been explored (Line 21), it means that s is fully expanded,
all states in the current search path P (including s) are marked as safe. The reason is that P
represents a path from the initial state s0 to s, thus clearly every state on the path can reach a
4.4. Complete Stateful Exploration 51
fully expanded state, namely s. Else it will be checked whether s has to be refined (Line 23).
This is the case if s is part of a cycle (unfinished) and does not reach a fully expanded state
(not safe). Refining a state means that additional transitions are explored from that state. The
current implementation simply fully expands s, by exploring all enabled transitions that have
not already been explored. Consequently s and all states leading to it can be marked safe (Line
24).
Notes on Correctness
As discussed above, this SDPOR algorithm satisfies the cycle proviso condition C2 . This leaves
to show that condition C1 is satisfied too. The SDPOR algorithm performs completely equal to
the C-SDPOR algorithm until an unsa f e and un f inished state s is backtracked. In that case s
will be fully expanded. Thus s.done will be a trivial persistent set. For all other states, persistent
sets are computed with the same reasoning as for the C-SDPOR algorithm, as described in the
previous section. If C-SDPOR satisfies condition C1 , so does SDPOR.
5. State Subsumption Reduction
A stateful search keeps a set of already visited states to avoid re-exploration of the same state.
Whenever a new state s is reached, it will be matched with the already visited states. The basic
stateful algorithm presented in Section 2.5 requires two states to be fully equal in order to be
matched. However, in the context of symbolic execution, where states consists of concrete and
symbolic state parts, this is an unnecessary strong condition. The reason is that symbolic ex-
pressions can be represented in many (actually infinitely) different ways, e.g. the expressions a
+ a and 2 * a are structurally different (hence not equal) but semantically equivalent. Further-
more, a path condition (pc) is managed during symbolic execution. It represents constraints that
the symbolic expressions have to satisfy, thus effectively limiting the possible values a symbolic
expression can assume. In general a symbolic expression represents a set of concrete values. A
symbolic state thus represents sets of concrete states. If the set of concrete states represented
by a symbolic state s2 contains the set of concrete states represented by a symbolic state s1 , it
is not necessary to explore s1 if s2 has already been explored. For example consider two states
s1 and s2 , that are equal except for a variable v and their path conditions, which are defined as:
v1 = 2 ∗ x, pc1 = x ≥ 3 ∧ x ≤ 7 and v2 = x + 5, pc2 = x ≥ 0 ∧ x ≤ 10 respectively. The symbolic
variable v1 represents the set of concrete values {6, 8, 10, 12, 14}, which is a subset of the values
{5..15} represented by v2 , in combination with their corresponding path conditions. It is unnec-
essary to explore s1 , if s2 has already been explored, since s2 subsumes all possible behaviors of
s1 . State s1 is said to be subsumed/covered by s2 in this case, denoted as s1 s2 . The example
shows, that detecting subsumption between states can further increase the state matching rate,
thus reducing the exploration of redundant states, compared to equivalence detection, which
would miss the reduction opportunity outlined in the example. This reduction technique will be
called State Subsumption Reduction (SSR).
Additionally, subsumption is a general concept that is not limited to symbolic values. It can
also be considered as a generalization of state equivalence and also applied to concrete state
parts to further increase the number of state matches. For example, it will be shown that under
certain conditions states can be matched with different simulation times. Furthermore comple-
mentary techniques like heap- and process- (thread) symmetry reduction, e.g. as described in
[Ios02], can also be considered a form of state subsumption. These techniques are not further
considered in this thesis, since threads are (currently) not dynamically created, thus the threads
and their heap references are already normalized.
The rest of this chapter begins by introducing necessary definitions. This includes a specifi-
cation of the state (subsumption) matching predicate and a formalization of execution states.
Then in Section 5.2 and Section 5.3 sufficient conditions will be presented, such that SSR can
be combined with basic model checking and POR, while preserving assertion violations. Fi-
nally a concrete implementation (algorithm) of the predicate is provided in Section 5.4. The
algorithm will first compare the concrete state parts, and if they match, then the symbolic parts
will be compared. An exact method is used to check subsumption of symbolic values.
5.1. Definitions
State Subsumption Reduction (SSR) can, similarly to Partial Order Reduction (POR), be defined
in terms of a reduced state space AR = (SR , s0 , T, ΔR , se ). A reduced state space AR can be
obtained from another state space A = (S, s0 , T, Δ, se ) (e.g. the complete state space AG ) by a
reduction function r, as described in Section 2.6.1.
Basically exploration of a state s is avoided, if a state s has already been explored with s s .
The states s and s will be called similar or matching in such a case. The state s is said to be
subsumed or covered by s . The effectiveness of the reduction greatly depends on the accuracy
of the predicate. Defining s s as s = s , i.e. complete equality, will yield no reduction at
all, thus AR = AG in this case. On the other hand it can obviously not be chosen arbitrarily in
order to preserve all properties of interest. For example simply returning True for all state pairs
will result in a reduced state space where only the transitions of the initial state are explored.
All the successors si of the initial state s0 would be detected to be matched with s0 , hence r(si )
would be the empty. So far has been used intuitively. The following formal definition of is
suitable to preserve assertion violations.
The binary relation defines a partial order on the state space. If s1 s2 and s2 has al-
ready been explored, then it is not necessary to explore s1 , in order to detect all safety property
violations. Result coverage can be extended to result equivalence.
The binary relation defines an equivalence relation on the state space. By definition it
immediately follows that equivalence reduced state space contains all states and transitions
of the coverage reduced state space. Equivalence is strictly stronger but a predicate deciding
equivalence might be easier to implement than one deciding coverage/subsumption. If either
s1 s2 or s1 s2 then the states s1 and s2 are said to be similar or matching.
It is useful to adapt the notion of reachability for such reduced state spaces, since often states s
are reached that have enabled transitions but are not explored, because a similar state has already
been visited. The following definition of reachability, denoted as weak reachability, defines the
execution of traces that allows to express paths more naturally when state subsumption matching
by means of the predicate is used.
5.1. Definitions 55
• s s and
• t1 ∈ r(s ) and
• let st be the successor of s when executing t1 , then s is weakly reachable from st by
the rest trace t2 ..tn of w.
The states s, s and s do not have to be different, since s s holds for all states s. Thus
every state s is weakly reachable from itself by the empty trace.
Let s and s be states in AR , s is weakly reachable from s in AR iff there exists a trace w
such that s is weakly reachable from s by w, as defined above.
Similar to the normal reachability, weak reachability is also a reflexive and transitive relation
over the state space. Weak reachability is a generalization of normal reachability. Thus for all
two states s1 , s2 it holds that, if s2 is reachable from s1 , then s2 is also weakly reachable from
s1 . The following example shall illustrate the notion of weak reachability. An abstract example
state space is shown in Figure 5.1. The subsumption matching relation is defined as:
= {(s9 , s6 ), (s7 , s3 )}
Thus s9 s6 and s7 s3 holds. The reachability relation R is the smallest transitive reflexive
closure satisfying:
Where (a, b) denotes that b is reachable from a. The weak reachability relation RW is the
smallest transitive reflexive closure satisfying R ⊆ RW and:
{(s9 , s6 ), (s7 , s3 )} ⊆ RW
Thus e.g. s4 is weakly reachable from s8 by trace CAD, which goes through the states s8 , s9 , s6 , s7 ,
s3 , s4 . And s7 is weakly reachable from s0 by trace CBA or BCA, which go through the states
s0 , s5 , s6 , s7 or s0 , s8 , s9 , s6 , s7 respectively.
Where time is the current simulation, pc denotes the path condition and vars is a name to
value mapping of all variables. For simplicity it is assumed, that the names of global and local
variables are disjunct. Thus a single mapping is sufficient to manage all variables. The mapping
notifications maps time deltas to events, so once a time delta reaches zero (in the delta or timed
56 5. State Subsumption Reduction
s0
A B
C
s1 s5 s8
B B C
s9
s2 s6
C A
s3 s7
s4
Figure 5.1.: Abstract state space illustrating the concept of weak reachability
notification phase), its associated event is triggered. The list threads contains the state (ts ≡
thread state) of each thread. It consists of the thread name (any unique identifier will do), a
location loc, one can think of it as program counter, a status and a wait condition await, which
is either the special value None or an event, that this thread awaits to become enabled. The
status of a thread is either: Enabled, Disabled or Terminated. A thread becomes terminated
when it finishes the execution of all its statements. A thread is disabled when it waits for an
event or is suspended. Else it is enabled. The variable values and path conditions are allowed
to be symbolic. All other state parts are concrete.
Remark. Timed notifications are not tracked in the state, since they can be reduced to normal
events. Each statement wait time n; can be equivalently rewritten as a sequence of state-
ments { notify e, n; wait e; } ,where e is a unique event, as described in Section 2.1.
A mapping m (vars and notifications are modeled as mappings) consists of a sequence of
items. Each item is a (key,value) pair. Every key can only appear once: ∀(k1 , v1 ), (k2 , v2 ) ∈
m : k1 = k2 =⇒ v1 = v2 . Each mapping m provides the operations keys={k | (k, ) ∈ m} and
values={v | ( , v) ∈ m}. Additionally the value of a mapping m can be retrieved using the
corresponding key k as get(m, k) = v. This operation is defined iff (k, v) ∈ m. It will often be
abbreviated as m[k]. A mapping, e.g. m = {(a, 1), (b, 2)}, will often be simply written as {a = 1,
b = 2} or just {a: 1, b: 2} to improve readability.
The operation linkedValues(m1 , m2 )={(v1 , v2 ) | (k1 , v1 ) ∈ m1 ∧ (k2 , v2 ) ∈ m2 ∧ k1 = k2 } is de-
fined on two mappings m1 and m2 and returns a sequence of corresponding value pairs, value
which are stored under the same key, of both mappings.
Remark. The actual implementation uses states that are a bit more complex then the one de-
scribed here. So for example it manages a callstack for each thread. Each callframe contains
a local program counter and a mapping of local variables. Also variable values are treated as
atomar entities in the state description provided here. Actually (variable) values can be more
complex compound data structures like classes, arrays and pointers. But all of them can be
recursively compared and matched (following the pointer references will lead to another value,
classes and concrete arrays just group multiple values, symbolic arrays can be specifically mod-
eled as atomar entities and stackframes contain named slots of variables). Thus the same prin-
ciples apply for them. But for the following discussion it is irrelevant, whether function calls
and complex objects are supported or not. Omitting those details simplifies the following pre-
sentation. The implementation however supports these constructs.
5.1. Definitions 57
The set VS denotes all symbolic literals and the set VC all concrete values. The combination
P = VS ∪ VC denotes the set of primitive values. A primitive value is either a bitvector or a
boolean value. The set Op denotes all (common) arithmetic and logic operators available in the
C++ language, e.g. {+, ∗, &&, !} ⊆ Op. The (infinite) set of expressions E can be recursively
defined. Every primitive value is an expression and applying an operator to a set of expres-
sions returns a new expression. Basically E is the set of expressions that can be constructed
from symbolic and concrete primitive values, combined with the available operators. Every
expression e ∈ E has a type. An expression is called primitive if it only consists of a primi-
tive value. Else it consists of an operator and a non-empty argument list. Such an expression
will be called compound. The predicates isPrimitive(e) and isCompound(e) can be used to test
an expression. The functions getOperator(e) and getOperands(e) return the operator or the
(ordered) tuple of operands respectively, for the compound expression e. Expressions with the
same operand always have the same number of arguments. The function zip can be used to
pair two tuples of arguments, since arguments are ordered. It is defined as zip(args1 , args2 ) =
{(a1 , a2 ) | a1 = args1 [i] and a2 = args2 [i] for i ∈ {1..|args1 |}}, where args[i] returns the i-th
element of the arguments tuple. Both primitive and compound expressions can naturally be
recursively compared by equality, e.g. x1 + 2 = x1 + 2 and x1 + 2 = 2 + x1 and x1 = x2 and so
on, where x1 , x2 ∈ VS are symbolic literals. Thus e1 = e2 iff matchExpressions(e1 , e2 ) returns
True as defined in Algorithm 12.
Definition 18 (Structural Compatibility (s1 ∼ s2 ))
Two states s1 and s2 are structurally compatible, denoted as s1 ∼ s2 , iff threads(s1 ) =
threads(s2 ) and notifications(s1 ) = notifications(s2 ) and time(s1 ) = time(s2 ) and vars(s1 ) ∼
vars(s2 ) and pc(s1 ) ∼ pc(s2 ).
vars(s1 ) ∼ vars(s2 ) iff keys(vars(s1 )) = keys(vars(s2 )) and ∀(v1 , v2 ) ∈ linkedValues(vars(s1 ),
vars(s2 )) : v1 ∼ v2 .
Two (primitive) values a,b are structurally compatible (a ∼ b) iff they may be equal. If a
and b are both concrete they may be equal iff a = b. If one of them is symbolic, they may
be equal if they have the same type.
So basically s1 ∼ s2 iff all concrete state parts are completely equal and symbolic state parts
may be equal. A necessary condition that they may be equal is to have the same structure.
Thus all variable values of structurally compatible states can be paired. And they will continue
with the same statements when resumed (though they can deviate from then on, since variable
values and path conditions are not restricted and thus execution of the states can take different
branches).
58 5. State Subsumption Reduction
Remark. If s1 ∼ s2 and neither s1 nor s2 contains any symbolic state parts then s1 = s2 .
The unary predicates isSymbolic and isConcrete denote whether a value is symbolic or con-
crete respectively. The function symbolicVars(s) = {v | v ∈ values(vars(s1 )) and isSymbolic(v)}
returns all symbolic variable values in the state s. It can be naturally extended to work on two
states, as the following definition shows:
Definition 19 (Symbolic Pairs)
1 int a = ?( int ) ; 10 19
2 int b = 0; 11 thread B { 20 thread C {
3 event e ; 12 if ( b != 0) { 21 notify e , 1;
4 13 b = ?( int ) + 2; 22 }
5 thread A { 14 } 23
6 a += 1; 15 assume ( b > 0) ; 24 main {
7 b += 1; 16 wait e ; 25 start ;
8 wait e; 17 assert ( b > 0) ; 26 }
9 } 18 }
The states s1 and s2 that are reached from the initial state by executing the transition sequences
A,B,C B,A,C
A,B,C and B,A,C respectively as s0 −−−→ s1 and s0 −−−→ s2 . They are formally defined as:
s1 = (0, x2 + 2 > 0, {a: x1 + 1, b: x2 + 2, e: e1 }, {1: e1 }, threads1 )
where threads1 = [(A, 3, e1 , Disabled), (B, 5, e1 , Disabled), (C, 2, None, Terminated)]
s2 = (0, T , {a: x1 + 1, b: 1, e: e1 }, {1: e1 }, threads2 )
where threads2 = [(A, 3, e1 , Disabled), (B, 5, e1 , Disabled), (C, 2, None, Terminated)]
The path condition of s2 has not been extended, since the condition ¬(b > 0) is unsatisfiable
because b has a concrete value in this case. The values of the thread locations (program coun-
ters) are somewhat arbitrary. Here just the line number offset to the beginning of the thread is
used. Any value that is sufficient to continue the threads execution correctly would be fine here.
The states s1 and s2 are structurally compatible, i.e. s1 ∼ s2 , since their concrete parts are
equal. Their symbolic state parts with respect to each other are defined as: ζ (s1 )=(x2 + 2 > 0,
{a: x1 + 1, b: x2 + 2}) and ζ (s2 )=(T , {a: x1 + 1, b: 1}). Which can also be equivalently written
as: ζ (s1 )=(pc: x2 + 2 > 0, a: x1 + 1, b: x2 + 2) and ζ (s2 )=(pc: T , a: x1 + 1, b: 1). Neither s1
nor s2 is structurally compatible with s0 since their concrete state parts are not equal, e.g. the
pending notification of event e is missing.
The function symbolicPairs(s1 , s2 ) returns {(a1 , a2 ), (b1 , b2 )} where a1 ,a2 and b1 ,b2 refer to
the corresponding variable values of a and b in s1 and s2 respectively. Then var1 (s1 ) = a1 =
x1 + 1 and var1 (s2 ) = a2 = x1 + 1 and var2 (s1 ) = b1 = x2 + 2 and var2 (s2 ) = b2 = 1.
The proof of this theorem is available in the Appendix in Section A.4. Since the subsump-
tion/coverage predicate yields state spaces which are a subset of those yield by the equiva-
lence predicate , which has been defined in Definition 15, the correctness of the equivalence
predicate is also covered.
C2W Let s be a state in AR . Let w = t1 ..tn be a non-empty trace leading to an error state from
s in AG . Then there exists a weakly reachable state s from s in AR , such that at least one
transition ti of w is in r(s ).
Please notice that the first transition t1 of w is enabled in s, i.e. t1 ∈ en(s), since by assumption
w leads to an error state in AG , which implies w is executable in AG . Requiring the reachability
of error states for the condition C2W is due to the definition of the state subsumption relation
, shown in Definition 14, since s1 s2 does not require that en(s1 ) ⊆ en(s2 ). Consequently,
a stateful state space exploration algorithm using the relation to match states could e.g. not
guarantee that r(s ) = 0/ if s s and s cannot reach an error state in AG . So basically condition
C2W prevents the ignoring problem for transitions that can lead to an error state. This is a
reasonable formulation, since transitions that (provably) cannot lead to an error state can safely
be ignored.
Condition C2W is strictly weaker than condition C2 , which were formulated in Section 3.1.3
during the presentation of partial order reduction. So C2 implies C2W and there exists complete
state spaces, where the former conditions will yield a smaller reduction (explore a larger state
space) than the latter conditions.
A reduced state space AR that satisfies C1 and C2W does preserve all assertion violations of
the corresponding complete state space AG . This result immediately follows from the following
theorem.
Theorem 5.3.1 (Assertion Violations Preserving Combined Reduction)
Let AR be a reduced state space where the reduction function r satisfies the conditions C1
and C2W as defined in this section. Let w be a trace in AG leading to an error state from the
initial state s0 . Then there exists a trace wr in AR such that an error state is weakly reachable
from s0 in AR .
A proof of this theorem is available in the Appendix in Section A.5. The conditions C1 and
C2W are sufficient to preserve assertion violations in the reduced state space. However, it might
be difficult to implement the condition C2W . Thus a stronger condition C2W S is proposed. In
S
C2W For every state s in AR there exists a weakly reachable state s from s in AR , such that s is
fully expanded, i.e. r(s ) = en(s ).
s0
A B
C
s1 s5 s8
D
B B C
s2 s6 s9 s10
C A A
s3 s7 z1
D D
s4 z2
Figure 5.2.: Abstract state space to demonstrate problems that can lead to unsoundness when combining
DPOR with SSR. The states z1 and z2 are, among others, not explored due to SSR. The error
state s10 can be potentially missed.
Nonetheless, On all conducted experiments, the SDPOR+SSR combination provided the same
result as the SPOR+SSR combination.
First Problem
Consider the abstract example state space in Figure 5.2. The states are visited in the order they
are numbered. The designated states z1 and z2 will, among others, not be explored due to SSR.
The SDPOR algorithm could miss the error state s10 by not exploring D from s8 .
A B C D
Assume that s9 is the currently explored state. So the paths s0 − → s1 −→ s2 −→ s3 − → s4 and
C B A B C
s0 −→ s5 − → s6 − → s7 have already been explored and s0 − → s8 − → s9 is the current search path P.
It has already been detected that s7 s3 and s9 is now matched with s6 , so s9 s6 holds (the
check s = s9 ∈ H in Line 3 returns True). Since s9 and s6 do not form a cycle, the SDPOR
algorithm will apply an extended backtrack analysis starting from the already visited state v =
s6 . The visible effects graph GV contains the following edges: [(s0 , A, s1 ), (s1 , B, s2 ), (s2 ,C, s3 ),
(s3 , D, s4 ), (s0 ,C, s5 ), (s5 , B, s6 ), (s6 , A, s7 ), (s0 , B, s8 ), (s8 ,C, s9 )]. So only the effect (s6 , A, s7 )
will be checked with the current path P for dependencies. The effect (s3 , D, s4 ) is missed entirely
since there is no path in GV from s6 to s3 . The reason is that s7 is not necessarily equal to s3 ,
but only the weaker condition s7 s3 holds. Thus the dependency of C and D in s8 is missed
B D
and the erroneous trace s0 − → s8 − → s10 is not explored.
A solution for this problem is to adapt the edges in the visible effects graph whenever a state
s is matched with an already visited state v. Since s is explored the first time, there is only
a single edge leading to s in GV , which has been recorded right before the recursive explore
call for s. So in Line 4 (of the SDPOR Algorithm 11), right after retrieving the already vis-
ited state v, the edge target should be replaced from s to v, e.g. by inserting the method call
GV .replaceEdgeTarget( f rom = id(s),to = id(v)), which performs this replacement. As al-
ready noted, only identifiers of states are stored and not the states themselves, so actually the
single edge whose target is id(s) is retrieved and its target is replaced by id(v). For this ex-
ample the edges (s6 , A, s7 ) and (s8 ,C, s9 ) would be replaced with (s6 , A, s3 ) and (s8 ,C, s6 ). So
starting from s6 the effect (s3 , D, s4 ) would also be collected and checked with C from s8 for
dependencies. Consequently D would be added to s8 .working and the error state s10 would be
detected.
5.4. State Matching 63
Second Problem
Normally, without SSR, the SDPOR algorithm would (eventually) explore the complete path
s0 ..z2 and then start backtracking. So C from s8 would be compared with D from z1 . A depen-
dency would be detected in this case and D would be added to the working set of s8 . With SSR,
C from s8 is compared with D from s3 , as already described above. The dependency still has to
be detected, else the error state s10 is missed.
Since the state subsumption relation is preserved by executing the same transition sequence,
as shown in Section A.4 in the Appendix, z1 s3 holds. The assumption z1 s3 does not
necessarily imply that the dependency of D with C in s8 is detected. Thus given two states s1
and s2 the requirement s1 s2 is not sufficient for the SDPOR algorithm to match s1 with s2 .
Additionally the following requirements are proposed:
D1 Every trace that can be executed from s1 in AG can also be executed from s2 in AG .
Where V E(s,t) returns the visible effects observed during the execution of transition t from
state s. Let e1 = V E(s1 ,t) and e2 = V E(s2 ,t) be visible effects. The effect set e1 is included in
e2 , written as e1 ⊆ e2 , if e1 .isDepenedent(e3 ) implies e2 .isDepenedent(e3 ) for all visible effects
e3 . Normally D1 should already follow from s1 s2 , but it is not necessary, so it is explicitly
listed. It has already been shown that s1 s2 is preserved for the successor states, whenever
the same transition is executed from s1 and s2 . Analogously the requirement D1 is preserved
too. It can be shown that the algorithms presented in Section 5.4.2 and Chapter 6 to decide state
subsumption s1 s2 for states s1 , s2 already satisfy the additional requirements D1 and D2 for
the combination of SDPOR and SSR.
Algorithm 13: Check whether a state sv ∈ H similar to s has already been visited.
Input: State s and hash map H of visited states
Output: True if a similar state to s has already been visited, else False
1 foreach sv ∈ H[hash(s)] do
2 if s ∼ sv then
3 if ζ (s) ζ (sv ) then
4 return True
5 return False
that have the same hash value, denoted as H[hash(s)]. Normally, only the concrete state parts
are hashed, because it is not easy to devise a hash function that would preserve the property
hash(s) = hash(sv ) =⇒ s = sv with sufficient precision (without filtering out too many states
sv in advance that would be matched with s when compared properly) for symbolic state parts,
since they do not have a canonical representation. Then s is compared with all sv ∈ H[hash(s)]
one after another. If s ∼ sv , so their concrete parts are equal, then their symbolic parts are
compared with one of the algorithms that will be presented in the following sections. If their
symbolic state parts are (detected to be) equivalent, then the states s and sv are equivalent. To
detect subsumed/covered states it would be sufficient to replace the condition ζ (s) ζ (sv ) in
Line 3 with ζ (s) ζ (sv ) (so basically swap the symbolic state part matching algorithm). The
Definition 14 and Definition 15 are naturally transferred to symbolic state parts.
Using a hash map is just an implementation detail, albeit a common one. It would also be
possible to store all visited states in e.g. a list though it would be less efficient. Common
optimization techniques like using multiple hash function for better filtering or incremental
hashing to speed up the computation of the hash function can also be used instead of using a
single hash value that is fully recomputed each time it is required2 .
As already mentioned both states are structurally compatible by assumption (s1 ∼ s2 ), thus they
have the same number of (primitive) variables all of which have the same type. Therefore the
variables can be matched, i.e. vari (s1 ) and vari (s2 ) refer to corresponding variables from both
states for i ∈ {1..k}. The terms v1 ..vk are fresh symbolic literals corresponding to the types
of the variables of s1 and s2 respectively, i.e. type(vi ) = type(vari (si )) = type(vari (si )) for
i ∈ {1..k}. The symbolic literals x1 ..xn and y1 ..ym are all symbolic literals that appear in either
2 At least for the hashing of concrete state parts, since as already mentioned symbolic state parts are not included
into the computation of the hash value due to the lack of a canonical representation.
5.4. State Matching 65
variable or path condition of state s1 or state s2 respectively, i.e. Γ (s1 ) = {x1 , .., xn } = and
Γ (s2 ) = {y1 , .., ym }. With the following auxiliary definition:
The exact symbolic subsumption can be equivalently defined as (making the correspondence
between vari (s1 ) and vari (s2 ) explicit):
Definition 20 (Exact Symbolic Subsumption (ESS))
A state s1 is covered/subsumed by a state s2 , i.e. s1 s2 according to Definition 14, if
ESS (s1 , s2 ) is valid. ESS (s1 , s2 ) is defined as:
⎛ ⎞ ⎛ ⎞
⎜ ⎟ ⎜ ⎟
⎜∃x1 ..xn : pc(s1 ) ∧ v1 = v f ⎟ ⎜ v2 = v f ⎟
⎝ ⎠ =⇒ ⎝∃y1 ..ym : pc(s2 ) ∧ ⎠
(v1 , ,v f ) ( ,v2 ,v f )
∈ f (s1 ,s2 ) ∈ f (s1 ,s2 )
where {x1 ..xn } = Γ (s1 ) and {y1 ..ym } = Γ (s2 ) are all symbolic literals that appear in either
variables or path condition in state s1 or s2 respectively.
In order to show that ESS is valid, it can be shown that ¬ESS is unsatisfiable. The formula
¬ESS (s1 , s2 ) can be equivalently rewritten as:
⎛ ⎞ ⎛ ⎞
⎜ ⎟ ⎜ ⎟
¬ESS (s1 , s2 ) = ⎜
⎝∃x1 ..xn : pc(s1 ) ∧ v1 = v f ⎟ ⎜
⎠ ∧¬ ⎝∃y1 ..ym : pc(s2 ) ∧ v2 = v f ⎟
⎠
(v1 , ,v f ) ( ,v2 ,v f )
∈ f (s1 ,s2 ) ∈ f (s1 ,s2 )
Which will be satisfiable if there exists an assignment of variable values that can be produced in
state s1 but not in s2 . If ¬ESS is unsatisfiable then ESS is valid which means s1 is subsumed
by state s2 . Any SMT solver with support for quantifiers can be used to check these quantified
formulas for satisfiability. In this thesis the Z3 solver is used.
A correctness proof, that s1 s2 holds if this method detects that s1 is subsumed by s2 , is
available in the Appendix in Section A.10. The idea is to show that every program line that can
be reached from state s1 can also be reached from state s2 , thus exploring s2 alone is sufficient
to detect all assertion violations3 .
Example
For example consider the symbolic state parts s1 =(pc: x1 = 0, a: 2 ∗ x1 , b: x2 ) and s2 =(pc:
T , a: y1 + 1, b: y2 + y3 ). All symbolic literals reachable from s1 are {x1 , x2 } and from s2 are
{y1 , y2 , y3 }. Both states (actually their symbolic parts) have two variables a and b, denoted as
a1 ,b1 and a2 ,b2 respectively. Both of them have bitvector type, e.g. 32 bit width4 . Thus two
fresh symbolic literals v1 and v2 will be introduced with corresponding types for a and b, i.e.
3 An assertion assert c; can always be rewritten as conditional statement if (!c) { assert false; },
which will reduce the checking of assertion violations to checking whether a specific program line can be
reached.
4 Choosing 32 bit width here is just an arbitrary decision for this example.
66 5. State Subsumption Reduction
Listing 5.2: Example program that demonstrates the usefulness of abstracting away the simulation time
when comparing states.
type(v1 ) = type(a1 ) = type(a2 ) and type(v2 ) = type(b1 ) = type(b2 ). Now the ¬ESS formula
can be formulated as:
[∃x1 , x2 : (x1 = 0) ∧ (2 ∗ x1 = v1 ) ∧ (x2 = v2 )] ∧
¬ [∃y1 , y2 , y3 : (T ) ∧ (y1 + 1 = v1 ) ∧ (y2 + y3 = v2 )]
and passed to the (Z3) SMT solver to check for satisfiability. This formula is unsatisfiable, thus
s1 is subsumed by s2 , denoted as s1 s2 .
1 thread A { 6 }
2 while ( @time < 3) { 7
3 wait_time 1; 8 main {
4 } 9 start 5;
5 assert false ; 10 }
Listing 5.3: Example program that explicitly retrieves the current simulation time.
1 event e ; 9 17 wait e ;
2 int n = 1; 10 thread B { 18 assert false ;
3 11 if ( n != 0) { 19 }
4 thread A { 12 wait_time 4; 20
5 n = 0; 13 } 21 main {
6 wait_time 4; 14 } 22 start 9;
7 notify e , 3; 15 23 }
8 } 16 thread C {
Listing 5.4: Example program that indirectly controls the program flow using wait time statement.
1 s15
C s2
... G
s12 n2 = (n1 + 1) % 3
s3
G C
2 s13 s4
C G
...
s10 s5 n3 = (n2 + 1) % 3
G C
3 s11 s6
... C G
s7 n4 = (n3 + 1) % 3
s8
G
4 s9
Figure 5.3.: Complete relevant (simulation time abstracted) state space for the program in Listing 5.2.
The states are numbered in the order a DFS search could explore the state space. For each
delta cycle, either the generator (G) or check (C) thread can be executed first. The resulting
state space would be similar. The interleavings are not partial order reduced, since G and C
CG
access (with G writing) the same global variable n. But both execution sequences s −−→ s
GC
and s −−→ s result in the same state s when started in the same state s. Thus (s11 , s6 ),
(s13 , s4 ) and (s15 , s2 ) are detected as equivalent states. For this reason neither successor of
(s11 , s13 , s15 ) has to be explored. Additionally (s9 , s1 ) and (s7 , s2 ) form cycles. Its necessary
to detect them, to prove that the program is indeed safe.
68 5. State Subsumption Reduction
lutely no influence on the simulation result (on the verification of the properties of interest) then
it is not necessary to track this part. It turns out to be quite simple to prove for the simulation
time (no time limit is specified and the current time is never retrieved from within the program).
6. Heuristic Symbolic Subsumption
A stateful search needs to decide whether a state has already been visited to avoid re-exploration.
This process is called state matching. The last chapter already presented a general state match-
ing algorithm that works in two steps. First the concrete state parts are matched. And only if
they are equal, the symbolic state parts will be checked for subsumption. An exact method,
called Exact Symbolic Subsumption (ESS), has been presented to detect subsumption between
symbolic state parts. The ESS method is exact but computationally very expensive to calcu-
late, especially due to the use of quantifiers. The reason for this complexity is that symbolic
expressions can be represented in many (actually infinitely) different ways, e.g. the expressions
a + a and 2 * a are semantically equivalent but structurally different. Furthermore, a symbolic
execution engine manages a path condition, that represents constraints the symbolic values have
to satisfy, thus effectively limiting the possible values a symbolic expression can evaluate to.
This chapter presents different heuristic methods to detect subsumption between symbolic
state parts more efficiently. They can be used instead of the ESS algorithm as part of the gen-
eral state matching algorithm, presented in Section 5.4. The goal is to improve the scalability of
the overall complete symbolic simulation, by balancing between state matching precision and
runtime overhead. It is based on the observation, that spending too much time with state match-
ing can slow down the overall simulation. Often it can be faster to re-explore some (equivalent)
part of the state space. Essentially two different heuristic approaches will be presented:
• Explicit Structural Matching (ESM) attempts to structurally match all corresponding ex-
pression pairs. It offers polynomial worst case complexity but its precision depends on
how well the expressions have been normalized in advance.
Both ESM and SBC are available in different configurations. The following descriptions assume
that the corresponding states are already structurally compatible (s1 ∼ s2 ), since they only focus
on the symbolic state parts. Furthermore the definitions with regard to an execution state, that
have been presented in the last chapter in Section 5.1.1, are also relevant for this chapter. As
already mentioned, subsumption is a generalization of equivalence, thus detecting equivalence
is also a form of subsumption detection. In the following the terms state subsumption and state
coverage will often be used interchangeably.
The rest of this chapter is organized as follows: First Section 6.1 and Section 6.2 present the
basic ESM and SBC methods respectively. Section 6.3 will introduce the fresh symbolic literal
problem. It describes a common situation, that arises when detecting subsumption without
using quantifiers, and cannot be handled with the basic ESM and SBC methods. Thus both
methods will subsequently be extended in Section 6.4 and Section 6.5 respectively, to solve the
fresh symbolic literal problem. Thereafter Section 6.6 classifies the ESM and SBC methods
together with their extensions by their state matching precision and provides some additional
remarks. The ESS algorithm of the previous chapter is also considered in the classification.
Finally Section 6.7 sketches some useful improvements for future work.
s0
V
s1
C R
s20 s2
R C
s3
s21 V
s4
C R
s18
s5
R C
s6
s19
V
s7
C R
s8 s11
R C
s9 s12
V V
s13
s10
C R
s14
R s17
s15
V
s16
Figure 6.1.: Complete partial order reduced state space for the program in Listing 6.1 for a maximum
count of three (max = 3). Due to POR it is only necessary to consider interleavings of the
threads C (count) and R (reset), since V (verify) is independent to both of them (in the initial
state s0 and later they are no longer co-enabled). This state space could be generated, if a
DFS executes the states according to their numbers starting with s0 .
72 6. Heuristic Symbolic Subsumption
During the simulation, all operations that only involve concrete arguments are directly folded
into a single concrete literal (bitvector or logic), e.g. (1 + 2) is directly evaluated to 3. Often
operations involve both concrete and symbolic arguments, e.g. ((x1 + 1) + 1) as in state s6 and
s19 reached during simulation of Listing 6.1. Clearly this expression can be simplified to (x +
2). A specialized rewrite rule, e.g. ((x + a) + b) → (x + (a + b)), can be added, where x will
match with any symbolic literal and a, b will match with any concrete literal1 .
Other similar expressions would benefit from the same simplification approach, e.g. ((1 +
x) + 1) or (2 + (3 * (x + 2))) and so on. It is possible to add specialized simplification rules
for all of them, but there are infinitely many possibilities. But the general idea is always the
same. Concrete arguments are reorganized and combined if possible. The idea is to separate the
concrete parts from the symbolic parts. The following rules allow to pull concrete expressions
to the right side, so that they can be folded. The assumption is that expressions are internally
held in binary form (the general idea is also applicable to argument lists). In the following
⊕ will denote an commutative operator and an associative operator. The usual associative,
commutative and distributive properties also apply to bitvector (integer modulo) arithmetic.
a ⊕ x → x ⊕ a (6.1)
(x1 ⊕ a) ⊕ x2 → (x1 ⊕ x2) ⊕ a (6.2)
(x1 ⊕ a) ⊕ (x2 ⊕ b) → (x1 ⊕ x2) ⊕ (a ⊕ b) (6.3)
(x1 ⊕ a) ⊕ (x2 ⊕ x3) → ((x1 ⊕ x2) ⊕ x3) ⊕ a (6.4)
(x a) b → (x (a b)) (6.5)
x1 (x2 a) → (x1 x2) a (6.6)
(x1 x2) (x3 a) → ((x1 x2) x3) a (6.7)
(x − a) + b → x + (b − a) (6.8)
(x + a) − b → x + (a − b) (6.9)
(x ∗ a) + (x ∗ b) → x ∗ (a + b) (6.10)
(x + a) ∗ b → (x ∗ b) + (a ∗ b) (6.11)
It is not directly apparent whether more complex terms like (x1 + a) ∗ (x2 + b) ≡ (((x1 ∗ x2) +
(x1 ∗ b)) + (x2 ∗ a)) + (a ∗ b) should be expanded or factored, to achieve a better normalization.
Depending on the use case (the actual program that shall be verified) it might be useful to add
some additional specialized simplification rules.
Using the above rules, expressions like e = (1+x1 )+(2∗((x2 +7)−4)) will no longer occur,
since they would already have been simplified. All expressions are simplified on construction.
Literal expressions are already fully simplified by definition. This implies, that every compound
expression constructor will receive argument expressions, which already are fully simplified
(with respect to the simplification rules). This allows to apply the rules locally and yield a fully
simplified expression, without a complete bottom up expression tree transformation. Thus when
attempting to construct e, the following (sub) expressions will be constructed and simplified
1A previous semantic analysis (or a similar mechanism) ensures that the operands are already type compatible.
6.1. Explicit Structural Matching 73
Listing 6.2: Example program that demonstrates the usefulness of symbolic simplification rules that fold
concrete arguments.
(construction is performed bottom up, starting at the leafs and proceeding to the root):
1 + x1 → x1 + 1 due to 6.1
x2 + 7 → x2 + 7
(x2 + 7) − 4 → x2 + 3 due to 6.9
2 ∗ (x2 + 3) → (x2 + 3) ∗ 2 due to 6.1
(x2 + 3) ∗ 2 → (x2 ∗ 2) + 6 due to 6.11
(x1 + 1) + ((x2 ∗ 2) + 6) → (x1 + (x2 ∗ 2)) + 7 due to 6.3
Listing 6.3: condition-builder, example program whose simulation would produce many equivalent
states, due to commutative operations.
a − b → a + (−b) (6.12)
a ≥ b → (a > b) ∨ (a = b) (6.13)
a = b → ¬(a = b) (6.14)
a ≤ b → (a < b) ∨ (a = b) (6.15)
a > b → ¬(a < b) ∧ ¬(a = b) (6.16)
The main advantage of this normalization, is that it reduces the number of possibilities to cre-
ate semantically equal expressions with syntactically different constructs. This leads to better
normalized expressions and thus a higher detection rate of the structural matching algorithm.
It also reduces the number of operators that need to be implemented. A slight disadvantage of
this approach is, that it may (minimally) reduce the simulation performance. The reason is that
a single operator expression is rewritten with multiple operators, e.g. (a > b) is rewritten to an
expression that contains five operators.
x + 0 → x x ∗ 0 → 0 x ∗ 1 → x x|0 → x
x|x → x x&x → x x ∧ T → x x ∧ F → F
x ∨ T → T x ∨ F → x x ∧ x → x x ∨ x → x
x ∧ ¬x → F x ∨ ¬x → T
It is sufficient to specify this rules once with the concrete operand being on the right side, since
it will be transfered to the right side as already described. The operators | and & denote bitwise
or respectively and operations.
The direct absorption rules x ∧ x → x and x ∨ x → x can be extended to work with a list of
6.1. Explicit Structural Matching 75
( xi ) ∨ x → ( xi ) if x j = x (6.18)
i∈{1...n} i∈{1... j−1, j+ j...n}
Doing so will remove any (syntactically) duplicate expressions from logic expressions. The
bitwise and and or operations can be rewritten analogously.
Adding the same term in negated form to a conjunction (or disjunction) list, results in a
contradiction (or tautology). The direct simplification rules are x ∧ ¬x → F and x ∨ ¬x → T .
Similarly to the absorption rules they can also be extended to work with a list of arguments
(x1 ...xn ).
( xi ) ∧ x → F if x = ¬x j (6.19)
i∈{1...n}
( xi ) ∧ x → T if x = ¬x j (6.20)
i∈{1...n}
Further Improvement
Another promising optimization, which has not yet been implemented, is to sort all expression
arguments based on some total order between expressions. This allows to normalize all com-
mutative operations, that have the same arguments but possibly in different order. An example
program that demonstrates the usefulness of such an approach is available in Listing 6.3.
The program is safe, it runs indefinitely but has a finite state space, since every possible
execution path runs into a cycle. In this case, the program will repeat its behaviour after k = 16
steps.
Essentially the program manages a boolean condition b = True and a symbolic integer n = x1 ,
which is initially constrained to be in the range [0..k − 1]. The thread C repeats the following
behaviour indefinitely: it runs for k steps and then verifies the constructed condition. In each
step C notifies the threads A and B. The threads A and B will update the condition b in each step
i (starting with 0, ending with k − 1), where i = (k − 1)/2 and i = (k − 1)/2, to either (A)
b ∧ (n = i) or (B) b ∧ (n = (k − 1 − i)). So bi+1 will have either the new value bi ∧ (n = i) ∧ (n =
A,B B,A
(k − 1 − i)) or bi ∧ (n = (k − 1 − i)) ∧ (n = i), depending whether −−→ or −−→ is executed.
Regardless of the scheduling decision, the same terms will be added to b, but they appear in
different orders. All terms can be arbitrarily rearranged since the logic and operator is commu-
tative. The resulting expression (after sorting the arguments) depends on the total order. For
this use case, the choice of the total order is actually completely irrelevant. Any arbitrary valid
order will suffice.
But even without detecting commutative operations, the program can still be proven correct,
because the number of different terms added to b is finite. Each term has the form n = j with
j ∈ ({0..(k − 1)/2 − 1} ∪ {0..(k − 1)/2 + 1}). Thus using the absorption simplification
rule 6.17 a fixpoint is reached after two iterations for each execution path (since each iteration
adds the same terms). It only takes (considerably) longer to explore the relevant state space
completely, since the execution paths that arise due to different scheduling decisions are not
pruned earlier.
76 6. Heuristic Symbolic Subsumption
The performance of the structural matching algorithm can be (significantly) improved, if one
can detect early that two expressions must have a structural incompatibility in which case they
cannot be matched.
A common practice to filter out necessary unequal objects early before the actual comparison
is to compute a hash value. The generic comparison Algorithm 13 does not do so, because
symbolic values can have many different syntactic representation with the same semantic. But
since the structural matching algorithm will only match completely equal expressions, it is easy
to define a hash function that preserves the property: hash(e1 ) = hash(e2 ) =⇒ e1 = e2 for all
expressions e1 and e2 , where e1 = e2 returns True if e1 and e2 would not match using the ESM
algorithm.
Thus two symbolic state parts ζ (s1 ) and ζ (s2 ) can only be equivalent, if all value pairs
(v1 , v2 ) ∈ ζ (s1 , s2 ) and both path conditions have the same hash value. Therefore the hash
value of the complete state can also include the hash values of the symbolic state part. This
allows the generic comparison Algorithm 13 presented in the overview Section 5.4.1 to filter
out necessarily non-equivalent states (states that will not be detected as equivalent with the
structural match method) early. The hash value can be computed once during the construction
of each expression effectively in constant time, since it has already been computed for every
child node.
The rational behind this optimization is based on the observation that two symbolic expres-
sions most often are not structurally identical. Thus it is especially useful, if many structurally
compatible states are generated (which means that symbolic state parts will be compared often)
and symbolic expressions grow quite large and do not directly mismatch.
The Explicit Structural Matching (ESM) algorithm, described in the previous section, strongly
depends on a good expression normalization. It is a non-trivial task to devise and implement
algorithms that normalize expressions extensively and efficiently. Instead it would be more
useful to leverage available SMT solvers to decide whether some (arbitrary) expressions are
semantically equivalent. The following description starts with a first algorithm, that reduces the
equivalence checking problem of symbolic state parts to the satisfiability of an SMT formula.
Similarly to the ESM algorithm, it will be assumed that the compared states are already struc-
turally compatible. So their concrete state parts are completely equal. Only their symbolic state
parts will be compared.
This section describes, how the equality checking of the symbolic parts of two execution states
s1 and s2 can be reduced upon the satisfiability of an SMT formula. To this end an SMT formula
is generated, that is satisfiable, if the state parts can be unequal. It implies, that if the formula is
unsatisfiable, then the state parts must be equivalent.
6.2. Solver-Based Comparison 77
Listing 6.4: rbuf, example program that demonstrates the benefits of a symbolic state comparison method
that is insensitive to the argument order of commutative operations.
Definition 21
Two symbolic state parts s1 and s2 are equivalent, i.e. s1 s2 according to Definition 15,
if F (s1 , s2 ) is valid.
F (s1 , s2 ) = (pc(s1 ) = pc(s2 )) ∧ ( v1 = v2 )
(v1 ,v2 )∈symbolicPairs(s1 ,s2 )
Similarly to the ESS method, as presented in Section 5.4.2, the formula F (s1 , s2 ) is valid, if
¬F (s1 , s2 ) is unsatisfiable.
An SMT solver can be queried in order to check the above (negated) formula for satisfiability.
The advantage of this state equivalence matching method is, as already stated, that it does not
require to normalize expressions in order to detect equivalences. Also it is insensitive to the
argument order of symmetric operations. For this reason it performs very well for e.g. the ex-
ample program in Listing 6.3.Since the current normalization method described in Section 6.1.2
does not sort expression arguments based on some total order, it misses many equivalent states
which the solver-based method does detect. Another example program which demonstrates this
observation even more clearly is shown in the following.
78 6. Heuristic Symbolic Subsumption
Again the formula F (s1 , s2 ) is valid, if its negation ¬F (s1 , s2 ) is unsatisfiable.
⎛ ⎞
By definition it immediately follows that F (s1 , s2 ) implies F (s1 , s2 ) and F (s2 , s1 ). But the
reverse is not true. Consider for example the symbolic state parts s1 =(pc: x1 > 5, a: x1 > 5 ?
4 : 1) and s2 =(pc: x1 > 5, a: x1 > 5 ? 4 : 2). They would not be detected equivalent by Defi-
nition 21 of F (s1 , s2 ), but both F (s1 , s2 ) and F (s2 , s1 ) are valid. Similarly for the symbolic
6.3. Fresh Symbolic Literal Problem 79
state parts s1 =(pc: (x1 > 9), a: x1 ) and s2 =(pc: (x1 > 5), a: x1 ), F (s1 , s2 ) is valid, whereas
F (s1 , s2 ) is not. Thus Definition 22 of F (s1 , s2 ) is a strictly weaker state matching criteria
than Definition 21 of F (s1 , s2 ). A complete example, where this heuristic coverage/subsump-
tion method would perform considerably better, than requiring equivalent states, is presented in
the following.
Listing 6.5: symbolic-counter, example program that demonstrates the advantages of a stateful explo-
ration that detects covered states instead of equivalent ones.
during the simulation. The tokenring program, shown in Listing 6.6, is a simple example that
demonstrates this behaviour.
The program is safe. Its complete state space is shown in Figure 6.2. It consists of two threads
master (M) and transmit (T), that communicate using a global variable token and two events E1
and EM. Initially both threads are enabled. If the simulation starts with the master thread, then
the notification E1 of the master thread will be lost, thus the execution path terminates after
M,T
executing the transmit thread. This case corresponds to the trace s0 −−→ s2 (in Figure 6.2).
Choosing transmit first and then master results in the state s4 . From here on, both threads
will alternately indefinitely. But they repeat the same behaviour in cycles. Thus the state space
is finite.
Every time the master thread is executed, it generates a fresh symbolic literal and assigns it
to the token variable. A copy is also stored locally. Before yielding control to the simulation
kernel, it enables the transmit thread. The transmit thread then increments the token variable
and notifies the master thread. The master thread then verifies the value of the token variable
and the cycle starts again.
The states s4 and s6 are equivalent. Their symbolic state parts are ζ (s4 ) = (pc = T,token =
x1 , local = x1 ) and ζ (s6 ) = (pc = T,token = x2 , local = x2 ). But they would not be detected
as such by the algorithms presented so far. The reason is that x1 and x2 are different symbolic
literals. Thus they would not be matched by the ESM method. But actually they should be,
since they are both unconstrained and have the same type. Similarly the SBC method will fail,
e.g. using the Definition 22 (using Definition 21 would fail similarly) to construct a formula for
state subsumption checking results in:
T =⇒ (¬T ∨ (x1 = x2 ))
This formula clearly is satisfiable, e.g. ψ = {x1 = 0, x2 = 1} would be a model, which means
the states will not be matched. The idea is to strengthen the formula with additional equality
assumptions between symbolic literals.
The fresh symbolic literals problem only arises in the context of heuristic methods, the ESS
algorithm does not have this problem, because every symbolic literal is bound by a quantifier.
The following two sections present extended versions of the ESM and SBC methods that can
handle fresh symbolic literals.
6.4. Extended Explicit Structural Matching 81
s0
M T
s1 s3
T M
s2 s4
T
s5
M
s6
Figure 6.2.: Complete state space of the tokenring example program in Listing 6.6 with one transmitter
thread. The dotted line from s6 to s4 represents, that these states are equivalent.
6 function matchExpressions(e1 , e2 , m) is
7 if isPrimitive(e1 ) ∧ isPrimitive(e2 ) then
8 return matchPrimitives(e1 , e2 , m)
9 else
10 return matchCompounds(e1 , e2 , m)
11 function matchPrimitives(e1 , e2 , m) is
12 if isConcrete(e1 ) ∧ isConcrete(e2 ) then
13 return e1 = e2
14 else if isSymbolic(e1 ) ∧ isSymbolic(e2 ) then
15 if type(e1 ) = type(e2 ) then
16 return False
17 else if e1 ∈ m then
18 if m[e1 ] = e2 then
19 return False
20 else
21 m.add((e1 , e2 ))
22 m.add((e2 , e1 ))
23 else
24 return False
25 return True
26 function matchCompounds(e1 , e2 , m) is
27 if getOperator(e1 ) = getOperator(e2 ) then
28 return False
29 return ∀a1, a2 ∈ zip(getOperands(e1 ), getOperands(e2 )) : matchExpressions(a1 ,
a2 )
6.5. Extended Solver-Based Comparison 83
6.4.2. Examples
All the following examples assume, that the (matched) symbolic literals have equal types. The
states s4 and s6 of the tokenring example with symbolic parts ζ (s4 )=(pc: T , token: x1 , local:
x1 ) and ζ (s6 )=(pc: T , token: x2 , local: x2 ) can be matched with the mapping m={(x1 , x2 )}. The
symbolic state parts ζ (s1 )=(pc: (x1 < 5) ∧ (x2 > 4), a: 2 ∗ (x1 + x3 ), b: x1 ∗ (x2 + 1), c: x1 < x3 )
and ζ (s2 )=(pc: (x1 < 5) ∧ (x4 > 4), a: 2 ∗ (x1 + x5 ), b: x1 ∗ (x4 + 1), c: x1 < x5 ) can be matched
with m={(x1 ,x1 ), (x2 ,x4 ), (x3 ,x5 )}.
The following example demonstrates why it is necessary to require that every symbolic lit-
eral is matched with at most one other symbolic literal. Consider two symbolic state parts
ζ (s1 )=(pc: T , v: x1 + 1, w: x2 + 1) and ζ (s2 )=(pc: T , v: x1 + 1, w: x1 + 1). Starting with an
empty mapping m and matching v1 with v2 results in m = {(x1 , x1 )}. Now matching w1 and w2
using m results in an inconsistent mapping m = {(x1 , x1 ), (x1 , x2 )}. Thus s1 and s2 are correctly
classified as non equivalent. Executing the statement assert(v == w) on both states indepen-
dently would pass on s2 but fail on s1 , e.g. ψ={x1 = 0, x2 = 1} would be a model that satisfies
v1 = w1 .
Definition 24
A symbolic literal mapping m between two states s1 and s2 is type-compatible iff all sym-
bolic literals that are mapped on each other have the same type (∀(a, b) ∈ m : type(a) =
type(b)).
Definition 25
A symbolic literal mapping (set of equality assumptions) m is consistent iff it is type-
compatible and not ambiguous. Else it is called inconsistent.
Given a set of consistent equality assumptions, the definitions 21 and 22 of the base algorithm
can be extended, in order to solve the fresh symbolic literal problem as discussed in the previous
section, as follows.
Definition 26
Two states s1 and s2 are equivalent, i.e. s1 s2 according to Definition 15, if there exists a
set of consistent equality assumptions m, such that F (s1 , s2 , m) is valid.
⎡ ⎤
F (s1 , s2 , m) = ⎣( x = y) =⇒ F (s1 , s2 )⎦
(x,y)∈m
Definition 27
A state s1 is covered/subsumed by a state s2 , i.e. s1 s2 according to Definition 14, if there
exists a set of consistent equality assumptions m, such that F (s1 , s2 , m) is valid.
⎡ ⎤
F (s1 , s2 , m) = ⎣( x = y) =⇒ F (s1 , s2 )⎦
(x,y)∈m
Again the formulas F (s1 , s2 , m) and F (s1 , s2 , m) are valid, if their negations ¬F (s1 , s2 , m)
and ¬F (s1 , s2 , m) respectively, are unsatisfiable.
⎡ ⎤
¬F (s1 , s2 , m) = ⎣( x = y) ∧ (¬F (s1 , s2 ))⎦
(x,y)∈m
⎡ ⎤
¬F (s1 , s2 , m) = ⎣( x = y) ∧ (¬F (s1 , s2 ))⎦
(x,y)∈m
This extension is a real generalization of the previous solver-based method (which itself can
be considered a generalization of the ESM method). Because if an empty symbolic literal map-
ping m = 0/ is used then both formulas are completely equal. Furthermore it is a special case
of the ESS method, i.e. for all states s1 , s2 in AG , if there exists a consistent set of equality as-
sumptions m, such that F (s1 , s2 , m) holds, then F (s1 , s2 , m) implies ESS (s1 , s2 ). This follows
immediately by definition. Whenever a value can be constructed in s1 , the equality assumptions
m can be used to transfer it to s2 . This is always possible, because m is consistent. Section A.1
6.5. Extended Solver-Based Comparison 85
in the Appendix presents two algorithms that can be used to generate sets of consistent equality
assumptions. Every one of these sets can be used as argument to the formula F or F to detect
(symbolic) state subsumption or equivalence.
This extended SBC method is now able to handle the fresh symbolic literal problem. The
symbolic state parts (pc: T , a: x1 ) and (pc: T , a: x2 ) of the tokenring example are now detected
to be equivalent using the equality assumptions m = {(x1 , x2 )}. Doing so results in the negated
formula ¬F (s1 , s2 , m) = [(x1 = x2 ) ∧ ((T = T ) ∨ (x1 = x2 ))], which is unsatisfiable. Another
example is ζ (s1 )=(pc: T , a: x1 ∗ x2 + x1 ) and ζ (s2 )=(pc: T , a: y2 ∗ (y1 + 1)). Using the equality
assumptions m1 = {(x1 , y1 ), (x2 , y2 )}, the formula ¬F (s1 , s2 , m1 ) is satisfiable with the model
ψ = {x1 = 0, x2 = 1, y1 = 1, y2 = 0}. But ¬F (s1 , s2 , m2 = {(x1 , y2 ), (x2 , y1 )}) is unsatisfiable.
Thus the states are equivalent.
is unsatisfiable, which incorrectly means that s1 is covered by s2 . This result is incorrect, be-
cause when the states are executed independently, then v1 = 0 and w1 = 1 is a possible valuation
in s1 , which has not been considered in the equality check, since the equality assumptions have
prevented it. Now if the statement assert (v == w) is executed from both states independently,
then it will fail in s1 but pass in s2 . This behaviour clearly contradicts with the definition of
s1 s2 .
The idea is to only allow equality assumptions, that do not restrict the values that the variables
can assume when the states would be executed independently (to show s1 s2 it is sufficient
to allow all possible values of s1 ). This ensures, that every possible value is considered by the
solver, when searching for a counter example that makes the states unequal. For this reason it
is required, that the equality assumptions are consistent (unambiguous and type-compatible).
Consistent equality assumptions do not restrict the values that variables of a state can assume.
Another example, that is correctly rejected as non-equivalent, is ζ (s1 )=(pc: T , a: x1 ∗ x2 + x1 ,
b: x1 ) and ζ (s2 )=(pc: T , a: y2 ∗ (y1 + 1), b: y1 ). It becomes necessary, that x1 is mapped on y1 4
for the linked variables b1 and b2 to be equal. For a1 and a2 to be equal, the equality assumptions
{(x1 , y2 ), (x2 , y1 )} are required. So all together the equality assumptions m={(x1 , y1 ), (x1 , y2 ),
(x2 , y1 )} are required, such that F (s1 , s2 , m) becomes unsatisfiable. But m is inconsistent (since
x1 is mapped on both y1 and y2 ), so it will not be considered. A consistent set of equality
assumptions m , that makes F (s1 , s2 , m ) unsatisfiable, does not exist for this example. So
these states would be correctly classified as non equivalent (e.g. executing assert (b != 0
|| a == 0); would pass in s1 but fail in s2 ).
4 Actuallythe mapping direction does not matter, since by definition equality assumptions are symmetric (since
equality is symmetric).
86 6. Heuristic Symbolic Subsumption
1 int NUM_ITERATIONS = 3; 34
2 int result = 0; 35 int fB () {
3 int step = 0; 36 int b1 = ?( int ) ;
4 event e ; 37 int b2 = ?( int ) ;
5 38 int b3 = b2 *( b1 + 1) ;
6 bool is_even ( int x ) { 39
7 return x % 2 == 0; 40 assume pred ( b2 ) ;
8 } 41
9 42 return b3 ;
10 bool pred ( int x ) { 43 }
11 return ( x == 2) || ( x == 4) ; 44
12 } 45 thread B {
13 46 while ( true ) {
14 int fA () { 47 if ( is_even ( step ) ) {
15 int a1 = ?( int ) ; 48 result = result + fB () ;
16 int a2 = ?( int ) ; 49 notify WRITE , 0;
17 int a3 = a1 * a2 + a1 ; 50 }
18 51 step += 1;
19 assume pred ( a1 ) ; 52 wait READ ;
20 53 }
21 return a3 ; 54 }
22 } 55
23 56 thread check {
24 thread A { 57 while ( true ) {
25 while ( true ) { 58 wait WRITE ;
26 if ( is_even ( step ) ) { 59 notify READ , 1;
27 result = result + fA () ; 60 }
28 notify WRITE , 0; 61 }
29 } 62
30 step += 1; 63 main {
31 wait READ ; 64 start ( N UM_ITERATIONS - 1) ;
32 } 65 assert is_even ( result ) ;
33 } 66 }
Listing 6.7: symmetric-accumulator, example program that generates multiple semantically equivalent
but structurally different states.
0 simulation time s0
C
s1
A B
s2 s15
B A
s3 s16
C C
1 s4
A B A B
s13 s5
B A B A
s14
s6
C C C C
2 s7
A B A B A B A B
s11 s8
B A B A B A B A
s12
s9
C C C C C C C C
3 s10
Figure 6.3.: Complete partial order reduced state space for the program in Listing 6.7 for three time
steps (0,1,2). Due to POR it is only necessary to consider interleavings of A and B, since
C is independent to both of them (in the initial state s0 and later they are no longer co-
enabled). Circles denote normal states, squares represent states without enabled threads so
they perform a delta or timed notification. All squares at the bottom represent terminal states.
The dashed connections between s3 − s16 , s6 − s14 and s12 − s9 indicate that these state pairs
are equivalent. The direction of the arrow points to the already visited state. The dotted
gray shaded states (connected with dotted lines) are not explored since they are successors
of equivalent states. This state space could be generated, if a depth first search executes the
path from s0 to s10 first. This diagram assumes, that simulation starts with thread C, starting
with either A or B would result in a similar state space exploration diagram.
88 6. Heuristic Symbolic Subsumption
Table 6.1.: Relevant state parts for the detection of the equivalent states in program Listing 6.7 as shown
in Figure 6.3
state data value
s3 step 2
pc (x1 = 2 ∨ x1 = 4)
result 0 + (x1 ∗ x2 + x1 )
s16 step 2
pc (y2 = 2 ∨ y2 = 4)
result 0 + (y2 ∗ (y1 + 1))
s6 step 4
pc (x1 = 2 ∨ x1 = 4) ∧ (y4 = 2 ∨ y4 = 4)
result 0 + (x1 ∗ x2 + x1 ) + (y4 ∗ (y3 + 1))
s14 step 4
pc (x1 = 2 ∨ x1 = 4) ∧ (x3 = 2 ∨ x3 = 4)
result 0 + (x1 ∗ x2 + x1 ) + (x3 ∗ x4 + x3 )
s9 step 6
pc (x1 = 2 ∨ x1 = 4) ∧ (y4 = 2 ∨ y4 = 4) ∧ (y6 = 2 ∨ y6 = 4)
result 0 + (x1 ∗ x2 + x1 ) + (y4 ∗ (y3 + 1)) + (y6 ∗ (y5 + 1))
s12 step 6
pc (x1 = 2 ∨ x1 = 4) ∧ (y4 = 2 ∨ y4 = 4) ∧ (x5 = 2 ∨ x5 = 4)
result 0 + (x1 ∗ x2 + x1 ) + (y4 ∗ (y3 + 1)) + (x5 ∗ x6 + x5 )
Algorithm Summary
The algorithm consists of three subsequent phases: a preprocessing-, a construction- and a
generation- phase. The first two phases create an intermediate representation, namely a set of
(equality) constraints. The generation phase then generates sets of consistent equality assump-
6.5. Extended Solver-Based Comparison 89
tions from it. Every of these sets can be used as argument to the formula F or F to detect
(symbolic) state subsumption or equivalence. A constraint is a pair of symbolic literal sets
(A,B). It denotes that any literal a ∈ A can be mapped upon any literal b ∈ B (or the other way
round since equality is symmetric). Constraints are reduced into smaller ones by splitting them
into multiple disjunct parts. This process is denoted as separation. For example (A, B) can be
separated into (A1 , B1 ) and (A2 , B2 ) where A1 ∩ A2 = 0/ and B1 ∩ B2 = 0. / Smaller constraints
result in the generation of a smaller number of equality assumption sets, which in turn leads to
a smaller number of queries to the SMT solver, thus improving the state matching efficiency.
However, too aggressive separation can reduce the state matching precision.
The preprocessing phase constructs the initial constraints and separates each of them inde-
pendently of the other ones yielding a set of preprocessed constraints. Basically it works by
considering every corresponding value pair (v1 , v2 ) between the symbolic state s1 and s2 one af-
ter another in isolation. Each of them is transformed into an initial constraint c = (A, B), where
A and B are assigned all reachable symbolic literals from v1 and v2 respectively. These initial
constraints will be separated, if A ∩ B = 0/ or some symbolic literals in A are type incompati-
ble with some symbolic literals in B. The construction phase than further separates the set of
preprocessed constraints by analyzing them all together yielding a simplified set of constraints.
Basically two constraints (A1 ,A2 ), (B1 ,B2 ) will be further separated into smaller constraints, if
any one of A1 ∩ A2 , A1 ∩ B2 , B1 ∩ A2 , or B1 ∩ B2 is not disjunct. The generation phase than
yields sets of consistent equality assumptions, by mapping every a ∈ A to a b ∈ B for every
simplified constraint (A,B). More details, including complete algorithm listings, are presented
in the Appendix in Section A.1.
Example
As an example, the equality assumptions m2 = {(x3 , y4 ), (x4 , y3 )}, that have led to the detection
of s6 and s14 to be equivalent for the symmetric accumulator example program in the previous
section, will be derived in the following using the (second) algorithm, which has been summa-
rized above.
First a set of preprocessed constraints is constructed. Both symbolic state parts have two
entries, that can be grouped to pairs: the path conditions and the values of the result vari-
ables. First the pair (pc(s6 ), pc(s14 )) is considered. Its initial constraint (A, B) is computed
as A = Γ (pc(s6 )) = {x1 , y4 } and B = Γ (pc(s14 )) = {x1 , x3 }. Since A and B neither contain
any type incompatible nor equal literals, the constraint is kept without further separation. Next
the pair (vars(s6 )[result], vars(s14 )[result]) is considered. In this case A = {x1 , x2 , y3 , y4 } and
B = {x1 , x2 , x3 , x4 } which is separated into the three constraints ({x1 }, {x1 }), ({x2 }, {x2 }) and
({y3 , y4 }, {x3 , x4 }), since A ∩ B = {x1 , x2 }. The set of preprocessed constraints for both pairs is
then {c1 =({x1 , y4 }, {x1 , x3 }), c2 =({x1 }, {x1 }), c3 =({x2 }, {x2 }), c4 =({y3 , y4 }, {x3 , x4 })}.
These preprocessed constraints are then considered one after another, to construct a set of
simplified constraints. W.l.o.g. the constraints are considered in the order c1 ..c4 during the
construction phase. Initially the result set is empty. First c1 is simply added to the result set.
Next c2 is analyzed. It will result in the separation of c1 into ({x1 }, {x1 }) and ({y4 }, {x3 }). Then
c3 is analyzed and simply added to the result set, since it has no common symbolic literals with
any so far collected constraint. Lastly the constraint c4 is analyzed and separated into ({y4 },
{x3 }) and ({y3 }, {x4 }), due to the already collected constraint ({y4 }, {x3 }). The resulting set of
simplified constraints is {({y4 }, {x3 }), ({x1 }, {x1 }), ({x2 }, {x2 }), ({y3 }, {x4 })}.
Remark. The result would be the same, even if the order in which the preprocessed constraints
are considered would be changed, e.g. if c4 is added before c2 , then c1 and c4 would be sep-
90 6. Heuristic Symbolic Subsumption
arated into ({x1 }, {x1 }), ({y4 }, {x3 }), ({y3 }, {x4 }). Then c2 would be discarded, since ({x1 },
{x1 }) is already available. Lastly c3 would normally be collected without further separating any
collected constraints, thus yielding the same result.
The generation phase then yields only a single set of equality assumptions, namely m={(y4 ,x3 ),
(y3 ,x4 )}. The implicit equality assumptions (x1 , x1 ) and (x2 , x2 ) are not part of m, since they are
equal anyway. Only explicit equality assumptions are kept6 . Using m, the formula F (s5 , s13 , m)
is unsatisfiable, thus s6 and s14 are equivalent.
ESM SBC-EQ
PSBC-EQ PSBC-CV
Figure 6.4.: The precision of different state matching methods depicted as partial order (CEQ is the
weakest and ESS the strongest method)
The formula
that is generated to check whether to symbolic state parts must be equal (see Definition 27) is
satisfiable (and other symbolic literal mappings are not possible). So the states are still correctly
rejected as non-equivalent after introducing fresh symbolic literals.
• CEQ PESM : By definition, PESM is stronger than CEQ, since CEQ is not using ex-
pression simplifications. Thus it is unable to handle the condition-builder example (List-
ing 6.3), since it is unable to detect the cycle of states without expression simplification
rules. The PESM method on the other hand can verify it.
• PESM ESM : By definition, PESM can only match equal symbolic literals, whereas
ESM can match type compatible symbolic literals. PESM fails on the tokenring example
(Listing 6.6), but ESM can handle it.
• PSBC-EQ/CV SBC-EQ/CV : By definition, the PSBC versions do not use additional
equality assumptions. None of PSBC-EQ/CV can handle the tokenring example, but both
SBC-EQ and SBC-CV support it.
• PSBC-EQ PSBC-CV : By definition, PSBC-EQ requires that both path conditions are
equal, while for PSBC-CV it is sufficient if one implies the other. PSBC-CV can detect
92 6. Heuristic Symbolic Subsumption
many state subsumptions when simulating the symbolic counter example (Listing 6.5)
whereas PSBC-EQ cannot, e.g. (pc : x1 < 3, c : x1 + 1) (pc : T, c : x1 + 1).
• ESM SBC-EQ : The symbolic literals matched by the ESM algorithm always form a
subset m of all possible consistent equality assumptions. The SBC-EQ algorithm is com-
patible with all consistent equality assumptions. Thus if the state parts do structurally
match with m, the solver will detect it too using the equality assumptions m. On the other
hand, ESM misses many equivalent states in the symmetric accumulator example (Listing
6.7) and fails to prove the time unbounded version of the rotating buffer example (Listing
6.4) to be safe. Though the validity of these results depends on the simplification/nor-
malization engine used. A simple example that will always fail with ESM but pass with
SBC-EQ is (pc : T, a : 5) (pc : (x1 = 5), a : x1 ).
The actual performance of the methods does not have to correspond to their precision, as
shown in Figure 6.4, though. So e.g. the runtime of the SBC methods can be slower than the
runtime of the ESM methods, due to the more complex solver queries. The extended versions,
compared to their base versions (e.g. ESM compared to PESM), offer a greater precision with
negligible runtime overhead for those cases, which both methods can handle. Thus the extended
versions seem to be always superior to the base methods (the ESM is even better for all cases,
but the SBC can spend longer time building equality assumptions - but for example where no
fresh symbolic literals are used, there is no need for equality assumptions thus SBC would
perform the same way as PSBC). Detecting covered states can further improve the performance
of a stateful state exploration, as the (already presented) symbolic counter example in Listing
6.5 suggests. Though proving an implication might be a harder problem for the solver, than
proving equality.
1 int f () { 9
2 int z = ?( int ) ; 10 thread A () {
3 if ( z < 5) { 11 int a = f () ;
4 return 1; 12 ...
5 } else { 13 a = abs ( a ) ;
6 return -1; 14 wait_time 0;
7 } 15 ...
8 } 16 }
Listing 6.8: Code snippet that illustrates the need for path condition simplification
sT sF
=
Figure 6.5.: Neither the forked paths sT and sF , nor any of their corresponding descendants (represented
by the cones) will be detected as equivalent, without simplifying their path condition.
be detected as equivalent. The principle is illustrated in Figure 6.5. Even though some of these
descendant states are equivalent, they will not be detected as such, since the path conditions
are not simplified. They keep all their constraints even if the symbolic literals involved can no
longer influence the program execution. Listing 6.8 shows a code snippet that can be used to
construct such a situation.
When thread A is executed, it will call function f , which will create a fresh symbolic literal
x1 and assign it to the local variable z. Since z is unconstrained, both paths of the following
conditional branch are feasible. Thus the execution of thread A will fork and both paths will
be continued independently, with accordingly extended path conditions, sT (x1 < 5) and sF
(¬(x1 < 5)). Once the function f returns, the local variable z will be destroyed and its contained
symbolic literal x1 becomes unreachable.
The result of f will be stored in the local variable a (either 1 or -1) of thread A. Then later a
will be overwritten with its absolute value, thus the value of a becomes equal (only 1) on both
forked paths (and their possible descendants that might have been spawned between Line 11 and
Line 13). Now assuming that the states of sT and sF have only been modified uniformly between
Line 11 and Line 13, then they are both completely equal except for their path conditions, which
are still incompatible: x1 < 5 ∈ pc(sT ) and ¬(x1 < 5) ∈ pc(sF ).
But x1 is unreachable in both states. And in neither path condition it is part of an expression
which contains reachable symbolic literals. This means it cannot longer be referenced from the
program execution and cannot influence the possible values of other reachable symbolic literals
in the path condition. Thus the whole terms x1 < 5 and ¬(x1 < 5) can be safely removed (or
94 6. Heuristic Symbolic Subsumption
replaced with the concrete value true) from the corresponding path condition. Doing so would
allow the scheduler to detect that both paths are equivalent at the context switch (Line 14).
Based on these observations the following definition seems to be valid.
Definition 28
A n-ary logic expression (condition) e in the path condition can be replaced with the con-
crete literal true, if neither argument of e contains a reachable symbolic literal. A symbolic
literal is reachable, if there exists any reachable reference to it, starting from any local or
global variable value.
The basic idea is summarized in Definition 28. To simplify the path condition it is necessary
to detect unreachable values. This problem is part of a typical garbage collection (G.C.) method.
Many different G.C. algorithms have been proposed in the literature [TC11; Aho+06]. Two
well-known methods are sketched in the following.
A conceptually simple method would be to periodically (and preferably incrementally) col-
lect a set of all reachable symbolic literals starting from each local and global variable slot. All
values not in this set are considered unreachable. This garbage collection. method is commonly
known as mark and sweep.
Another possibility is to keep a reference counter for each value. Whenever a value is over-
written or destroyed, its counter will be decreased. When it hits zero, then all reference counters
of all reachable values will also be decreased. A symbolic literal with a zero count is consid-
ered unreachable. Reference count methods need a way to handle unreachable reference cycles,
which prevents any node of the cycle to reach a zero count.
* * + +
2 + 2 + x1 * x2 *
x1 x2 x3 x4 5 x1 x3 x4
+ +
x1 * x4 +
x2 + * x5
x3 1 x5 x6
Figure 6.6.: The upper left expression trees for the values v1 = 2 ∗ (x1 + x2 ) and v2 = 2 ∗ (x3 + x4 ) can
be completely matched. Doing so will generate the two constraints ({x1 }, {x3 }) and ({x2 },
{x4 }). The upper right trees for the values v1 = x1 + (5 ∗ x1 ) and v2 = x2 + (x3 ∗ x4 ) will
result in an inconsistent mapping, since the symbolic literal x1 is matched with both x2 and
x4 . The expression trees in the lower diagram for the values v1 = x1 + (x2 ∗ (x3 + 1)) and
v2 = x4 + ((x5 ∗ x6 ) + x5 ) can be partially matched. Only one constraint ({x1 }, {x4 }) will be
generated.
Example 3. Assuming two states s1 and s2 shall be matched, and both of them have uncon-
strained path conditions and two linked symbolic values v and w, that correspond to the up-
per left and lower middle examples of Figure 6.6. So v1 = 2 ∗ (x1 + x2 ), v2 = 2 ∗ (x3 + x4 ),
w1 = y1 + (y2 ∗ (y3 + 1)), w2 = y4 + (y5 ∗ y6 + y5 ). Further assuming that only type based group-
ing is employed and all symbolic literals of the pairwise linked values have the same type, then
the solver-based method alone would generate the following constraints:
({x1 , x2 }) - ({x3 , x4 })
({y1 , y2 , y3 }) - ({y4 , y5 , y6 })
Thus it would be possible to generate 12 (complete) symbolic literal mappings using the (sec-
ond) algorithm presented in Section A.1.3. But with the additional constraints ({x1 }, {x3 }),
({x2 }, {x4 }), ({y1 }, {y4 }) collected by the ESM preprocessing, the resulting constraints would
be:
({x1 }) - ({x3 })
({x2 }) - ({x4 })
({y1 }) - ({y4 })
({y2 , y3 }) - ({y5 , y6 })
Doing so would reduce the number of (complete) mappings to 2, namely m1 = {(x1 , x3 ), (x2 , x4 ),
(y1 , y4 ), (y2 , y5 ), (y3 , y6 )} and m2 = {(x1 , x3 ), (x2 , x4 ), (y1 , y4 ), (y2 , y6 ), (y3 , y5 )}.
7. Experiments
In this thesis a complete symbolic simulation has been proposed to verify SystemC programs
with arbitrary finite state space. Two complementary optimization techniques, namely POR
(Chapter 3-4) and SSR (Chapter 5-6), have been integrated to alleviate the state explosion prob-
lem and thus allow for the verification of non-trivial programs. Both, POR and SSR, are avail-
able in different variants. This section compares the different configurations with each other
and with state of the art tools in the verification of SystemC programs.
The experiments are performed on a Linux PC with a 1.4 GHz Intel Dual Core and 6GB ram.
The time and memory limits are set to 500 seconds and 2GB respectively. The abbreviations
T.O. and M.O. denote that the time and memory limit has been exceeded, respectively. The
abbreviation N.S. denotes that a program is not supported by a specific configuration. This
happens when a stateless algorithm is applied on a cyclic state space, where not every execution
path eventually runs into an error state, or a SSR method is used that is unable to detect a relevant
cycle in the program. The abbreviation N.A. denotes that some information is not available for
a specific configuration, e.g. the number of state matches in a stateless search. The runtimes are
specified in seconds.
The rest of this chapter is structured as follows: First the different configurations of POR and
SSR are presented. Then the benchmarks used in the subsequent experiments will be shown.
Next the effect of symbolic state part hashing in the ESM method is evaluated. Thereafter
the different supported solvers are compared, followed by the different POR variants and state
matching methods. Based on the observed results some configurations will be selected for the
main benchmark, that compares these methods against the Kratos model checker [CNR13]. As
already mentioned, Kratos represents one of the current state of the art approaches in SystemC
verification.
POR SSR
latter is based on the SDPOR algorithm, as described in Section 4.4. Thus in the following
all stateful explorations with static POR use the AVPE algorithm and those with dynamic POR
use the SDPOR algorithm. The SDPOR algorithm can optionally use a happens before relation
(HBR) to infer smaller persistent sets and fall back to static persistent sets whenever the dynamic
inference of a non trivial persistent set fails.
Static persistent sets can be computed using either the conflicting transitions (CT) or stubborn
set algorithm. The latter is available in two different configurations denoted as STUB1 (or
simply STUB) and STUB2 . They differ in the way how necessary enabling transitions are
computed whenever a dependency to a disabled transition is detected during the computation
of the persistent set. All of these three algorithms can compute a minimal persistent set locally
for each state. This extension will simply compute all possible persistent sets for a given state
(one for each enabled transition) and then select the smallest one. The suffix -MIN will be used
to denote that a minimal persistent set is computed. Thus the configuration STUB-MIN means
that the smallest persistent set shall be computed using the stubborn set algorithm.
Different state matching predicates have been implemented as described in Chapter 6 for the
State Subsumption Reduction (SSR). Basically they can be divided in explicit and solver-based
methods. The former are more efficient (polynomial worst case complexity) but less precise
than the latter. A classification of the methods is available in Section 6.6.
The explicit methods are the Complete Equality Matching (CEQ) and the Explicit Structural
Matching (ESM). The former requires that two states are completely equal, whereas the latter
involves expression simplification rules, and can handle the fresh symbolic literals problem,
as introduced in Section 6.3, compared to CEQ. The ESM can be seen as a generalization
of the CEQ method. The ESM method can optionally include symbolic state parts into the
computation of a state’s hash value, as described in Section 6.1.3. This variant of ESM will be
denoted as ESMH . It can result in a smaller number of unnecessary state comparisons.
The solver-based (SBC) methods are the heuristic (symbolic) equivalence (SBC-EQ), heuris-
tic (symbolic) subsumption/coverage (SBC-CV) and exact (symbolic) subsumption/coverage
(ESS). All of them query an (SMT) solver to decide whether two states match. The simulator
supports different solvers through metaSMT [Hae+11] and direct access of the Z3 solver. The
ESS method is always used in combination with the direct Z3 solver, since it is currently the
only supported solver that provides support for quantifiers, as required by the ESS method. The
other methods can use any of the available solvers.
Remark. The heuristic SBC methods require an algorithm that computes consistent equality
assumptions. Two different algorithms have been presented in Section A.1. The first one has
already been identified as not scalable, thus it will not be further considered. The second al-
gorithm itself can be further configured. Symbolic literals can either be matched by type or
by their program location, when generating constraint sets from corresponding (symbolic) vari-
able pairs. A constraint set consists of two sets of symbolic literals (A, B) and denotes that any
symbolic literal in A can be mapped upon any other symbolic literal in B. Assuming w.l.o.g.
|A| < |B|, all symbolic literals in A will be mapped on exactly one (unique) symbolic literal
in B, resulting in a set of consistent equality assumptions. The algorithm can either accept
all constraints or only regular ones, as described in Section 11. A regular constraint is one
where |A| = |B|. Accepting all constraints in combination with type matching is the most pre-
cise heuristic configuration, whereas matching by program location and accepting only regular
constraints should result in fewer unnecessary sets of equality assumptions. Experimental eval-
uation of these additional heuristics is postponed for future work. In the following the default
configuration, which is matching by type and accepting all constraints, is used.
7.2. Benchmark Overview 99
Table 7.1.: List of (unparameterized) benchmarks. Some benchmarks will be abbreviated with the op-
tional short name, due to page size constraints.
Benchmark Short name Version Source
bist-cell S [CNR13]
buffer S [Le+13]
condition-builder cb S Listing 6.3 in Section 6.1.2
counter S this section
kundu S [KGG08]
kundu-bug-1 U [CNR13]
kundu-bug-2 U [CNR13]
mem-slave-tlm mst S [CNR13]
mem-slave-tlm.bug mst.bug U [CNR13]
mem-slave-tlm.bug2 mst.bug2 U [CNR13]
pc-sfifo-sym-1 S [CNR13]
pc-sfifo-sym-2 S [CNR13]
pressure-safe pres-safe S [BK10]
rbuf S Listing 6.4 in Section 6.2.1
rbuf2 S this section
rbuf2-bug1 U this section
rbuf2-bug2 U this section
simple-fifo sfifo S [GLD10; Le+13]
simple-pipeline S [CNR13]
symbolic-counter sym-cnt S Listing 6.5 in Section 6.2.2
symmetric-accumulator sym-acc S Listing 6.7 in Section 6.5.1
term-counter S this section
token-ring S [CNR13]
token-ring-bug U [CNR13]
token-ring-bug2 U [CNR13]
toy-sim S [CNR13]
transmitter U [CNR13]
transmitter-safe S safe variant of transmitter
Remark. All heuristic methods are available in a plain and extended version as described in
Section 6.6. The difference is, that the extended versions can handle the fresh symbolic literal
problem, as described in Section 6.3. When applied to a benchmark, where the problem does
not occur, both methods perform similarly. But the extended versions can also handle programs
where fresh symbolic literals are introduced. Thus only the extended versions will be further
considered.
b7 b6 b5 b4 b3 b2 b1 b0 b3 b2 b1 b0 b7 b6 b5 b4
b3 b2 b1 b0 b7 b6 b5 b4 b3 b6 b5 b4 b7 b2 b1 b0
Figure 7.2.: The rotating (bit-) buffer benchmark is available in two configurations rbuf and rbuf2. In
every time step a new value is computed based on the current value of the buffer. The left
figure shows the principle operation of the rbuf benchmark and the figure on the right for
the rbuf2 benchmark.
Adapted Benchmarks
pressure-safe The program pressure appeared in [BK10]. Basically a thread increments a
counter while another thread guards the counter from exceeding a maximum value. However,
there exists some thread interleavings such that the condition is violated. The pressure-safe
program limits the simulation time to ensure that the violation does not occur. [BK10] has al-
ready observed that both threads are effectively non-interfering most of the time.f However this
effective non-interference will normally not be detected, neither by SPOR nor DPOR, because a
read-write dependency exists1 . A stateful search however can avoid re-exploration of the equiv-
alent states.
buffer The buffer program first fills an array using multiple threads and then checks the re-
sult. All threads write to a different memory locations, thus they are non-interfering. It is based
on the buffer-ws-pX benchmark from [Le+13] and has been modified to run without a simula-
tion time limit. Consequently, it has a cyclic state space and cannot be verified with a stateless
method.
simple-fifo The simple-fifo benchmark consists of a configurable number of consumer and pro-
ducers, that alternately write-to and read-from a shared fifo, which is implemented as an array.
other benchmarks The toy-sym, pc-sfifo-sym-1 and pc-sfifo-sym-2 benchmark respectively are
originally based on the toy, pc-sfifo-1 and pc-sfifo-2 benchmarks respectively. The concrete
counter values have been replaced with symbolic ones, to utilize symbolic state subsumption
matching. The simple-pipeline benchmark is based on the pipeline benchmark. The latter con-
tains unnecessary statements (e.g. notification of events that are never used in the program),
which have been removed. The transmitter-safe benchmark is a safe version of the transmitter
benchmark.
New Benchmarks
rbuf variants The rotating bit-buffer program is available in two different configuration. The
first rbuf, which has already been described in Section 6.2.1, uses either two or four threads to
rotate a 32 bit unsigned value c in each step into a new value n. Thus n[31..16] = c[15..0] and
n[15..0] = c[31..16] when two threads are used and n[31..24] = c[23..16], n[23..16] = c[15..8],
n[15..8] = c[7..0], n[7..0] = c[31..24] for the benchmarks with four threads. The second rbuf2
1 The guard thread will read the counter in each step, even though it will not modify it most of the time.
7.3. Comparing Hashing Methods for Symbolic State Parts 101
Listing 7.1: term-counter, example program that generates many states with equal concrete state parts
but different symbolic state parts
manages a signed 32 bit value. It rotates all but the first bit of each section. Thus n[30..16] =
c[14..0], n[14..0] = c[30..16] and n[31] = c[31], n[15] = c[15] for two threads. The version
with four threads is defined analogously. The principle is shown in Figure 7.2. Both of them
check that the number of high bits is equal before and after each rotation step. The program is
available in with and without a simulation time bound, e.g. the rbuf-4.5 is the first version with
four threads simulated five time steps (a single rotation step is performed in each time step).
And e.g. the program rbuf2-2 is the second version with two threads and no simulation time
limit. The rbuf2 program is also available in two unsafe variants, denoted rbuf2-bug1.2 and
rbuf2-bug2.2, which rotate the bits incorrectly in each step.
term-counter The term-counter program is shown in Listing 7.1. It is a safe program with
acyclic state space. It contains two (independent) threads that count up to a specified limit.
A clock thread re-enables them in each time step. The number of threads and the count limit
can be parameterized to create different benchmarks. Some results obtained by running this
program with 1..4 threads and with 50, 100, 200 and 400 steps, are shown in Table 7.2.
counter The counter program is similar to the term-counter program. It also has one to four
independent threads that count up to a specified limit. But the clock thread, that notifies the
counter threads in each step, will not terminate once all threads are done but keeps sending
notifications. Thus cycle detection, e.g. by using a stateful search, is required to verify it.
Table 7.2.: Evaluating symbolic state part hashing in a stateful search - first part
static POR no POR
Benchmark stateless stateful stateful
DFS ESMH ESM ESMH ESM
Total execution time
term-counter-1.50 1.494 2.089 2.949 1.887 2.735
term-counter-1.100 1.970 3.089 6.588 2.801 6.331
term-counter-1.200 3.045 5.141 19.515 4.932 18.901
term-counter-1.400 5.840 10.491 67.150 10.180 66.846
term-counter-2.50 2.109 3.048 4.306 4.110 6.532
term-counter-2.100 3.160 4.839 10.212 7.616 17.367
term-counter-2.200 5.884 9.314 30.964 17.309 56.174
term-counter-2.400 14.548 21.972 108.884 47.385 201.798
term-counter-3.50 2.975 4.266 6.362 14.768 23.059
term-counter-3.100 4.879 7.187 15.102 28.466 56.607
term-counter-3.200 10.331 15.245 46.754 70.697 179.097
term-counter-3.400 28.861 39.778 165.537 215.655 T.O.
term-counter-4.50 4.445 6.306 8.985 136.086 320.093
term-counter-4.100 7.543 11.238 21.691 202.778 445.940
term-counter-4.200 17.043 24.240 66.046 412.263 T.O.
term-counter-4.400 50.090 65.849 233.793 T.O. T.O.
Table 7.3.: Evaluating symbolic state part hashing in a stateful search - second part
static POR no POR
Benchmark stateless stateful stateful
DFS ESMH ESM ESMH ESM
Number of hash matches
term-counter-1.50 N.A. 0 3580 1 3581
term-counter-1.100 N.A. 0 14655 1 14656
term-counter-1.200 N.A. 0 59305 1 59306
term-counter-1.400 N.A. 0 238605 1 238606
term-counter-2.50 N.A. 0 4774 79 8602
term-counter-2.100 N.A. 0 19524 129 34577
term-counter-2.200 N.A. 0 79024 229 139027
term-counter-2.400 N.A. 0 318024 429 557927
term-counter-3.50 N.A. 0 6027 1292 27596
term-counter-3.100 N.A. 0 24452 1542 91771
term-counter-3.200 N.A. 0 98802 2042 347621
term-counter-3.400 N.A. 0 397502 3042 T.O.
term-counter-4.50 N.A. 0 7523 53737 541429
term-counter-4.100 N.A. 0 29623 54587 699529
term-counter-4.200 N.A. 0 118823 56287 T.O.
term-counter-4.400 N.A. 0 477223 T.O. T.O.
7.3. Comparing Hashing Methods for Symbolic State Parts 103
Table 7.4.: Evaluating symbolic state part hashing in a stateful search - third part
static POR no POR
Benchmark stateless stateful stateful
DFS ESMH ESM ESMH ESM
Number of explored transitions
term-counter-1.50 163 163 163 165 165
term-counter-1.100 313 313 313 315 315
term-counter-1.200 613 613 613 615 615
term-counter-1.400 1213 1213 1213 1215 1215
term-counter-2.50 233 233 233 377 377
term-counter-2.100 433 433 433 677 677
term-counter-2.200 833 833 833 1277 1277
term-counter-2.400 1633 1633 1633 2477 2477
term-counter-3.50 326 326 326 1262 1262
term-counter-3.100 576 576 576 1962 1962
term-counter-3.200 1076 1076 1076 3362 3362
term-counter-3.400 2076 2076 2076 6162 T.O.
term-counter-4.50 467 467 467 7511 7511
term-counter-4.100 767 767 767 9211 9211
term-counter-4.200 1367 1367 1367 12611 T.O.
term-counter-4.400 2567 2567 2567 T.O. T.O.
them. This section evaluates the performance benefits of doing so. A benchmark is particularly
good suited for this case, if it satisfies the following requirements:
• Usage of simple expressions to avoid spending too much time solving complex con-
straints.
• Many different transition interleavings lead to states that have equal concrete state parts
but different symbolic state parts.
The term-counter benchmark satisfies all of these requirements. It contains two (independent)
threads that count up to a specified limit. A clock thread re-enables them in each time step.
The number of threads and the count limit can be parameterized to create different benchmarks.
Some results obtained by running this program with 1..4 threads and with 50, 100, 200 and
400 steps, are shown in Table 7.2, Table 7.3 and Table 7.4. The table shows the total execution
times, the number of transitions explored and the number of hash matches between a currently
explored state and one already visited. For each hash match, the states will be explicitly com-
pared to determine whether they are equal.
A stateless POR search is an optimal configuration for this set of benchmarks, since it is suf-
ficient to prune all redundant transition interleavings between the counter threads. Combining
POR with a stateful search will not further reduce the explored state space, but add additional
overhead. A stateful search will store visited states and compute hash values for them to ensure
a more efficient lookup.
The POR with ESMH variant performs second best and is not far away from the optimal
stateless POR configuration. It shows an improvement of up to x6.5 compared to its ESM
counterpart. Similar improvements can be observed between non partial order reduced ESM
variants. Longer running simulation show greater improvements, due to the higher number
104 7. Experiments
of hash matches which lead to explicit comparison of the states. Once the complexity of the
analyzed program increases, the observed improvements diminish gradually, even though the
number of hash matches increases. Apparently the simulation of transitions, which involves
cloning of complex object structures, becomes more costly than the comparison of states.
Having only a single counter thread, thus POR will have no reduction effect, the stateful
searches without POR finish slightly faster. The reason is that they do not apply a preliminary
static analysis to obtain transition interference relations necessary for POR at runtime.
Result summary The ESM method with symbolic state part hashing, denoted as ESMH ,
shows the overall best results. Significant improvements can be observed compared to the
standard ESM version because the number of spurious hash matches is greatly reduced.
former involves non trivial logic expressions, whereas the latter involves some complex bitwise
operations. A SPOR DFS with explicit structural matching (SPOR+ESM) is used as the base
configuration.
The boolector solver accessed through metaSMT shows the overall best results, thus it will
be used as the default solver in the following. The native Z3 solver can be considered second
best. Since it is currently the only supported solver that provides support for quantifiers, it
will also be used. The next section compares different state matching methods. Most of them
involve the solver to check whether two symbolic state parts match (SBC-EQ/SBC-CV/ESS).
Thus the employed solver has to handle a greater number of requests, which can also be more
complex. Some configurations will use both the native Z3 solver and the boolector solver,
therefore comparing them incidentally.
Table 7.6.: Comparison of different state subsumption methods, all employ SPOR
SBC
Benchmark CEQ ESMH ESS
EQ CV EQ+Z3 CV+Z3
condition-builder.8 N.S. 4.580 2.071 2.091 4.809 4.821 5.064
condition-builder.16 N.S. 112.111 2.970 2.989 9.276 9.011 9.696
condition-builder.32 N.S. T.O. 5.179 5.233 18.165 18.510 19.593
counter-1.50 2.192 1.901 14.541 14.364 67.205 70.651 87.353
counter-1.200 4.478 4.685 T.O. 491.321 T.O. T.O. T.O.
counter-2.50 2.491 2.709 37.237 36.295 99.759 109.605 137.834
counter-2.200 8.075 8.437 T.O. T.O. T.O. T.O. T.O.
counter-3.50 3.502 3.818 75.000 73.043 146.316 161.465 198.165
counter-3.200 13.146 13.650 T.O. T.O. T.O. T.O. T.O.
counter-4.50 5.226 5.617 136.663 134.000 216.651 247.212 274.823
counter-4.200 21.182 21.590 T.O. T.O. T.O. T.O. T.O.
pc-sfifo-sym-1 T.O. T.O. T.O. T.O. T.O T.O. 1.877
pc-sfifo-sym-2 T.O. T.O. T.O. T.O. T.O T.O. 2.166
rbuf2-2 N.S. N.S. 10.084 10.359 4.741 4.736 3.238
rbuf2-4 N.S. N.S. 92.924 93.553 22.115 22.091 7.177
rbuf-2 N.S. N.S. 26.486 26.375 5.362 5.446 3.974
rbuf-2.1 1.913 1.890 1.899 1.959 2.216 2.235 2.212
rbuf-2.3 26.828 27.784 25.936 26.490 5.223 5.210 5.219
rbuf-2.5 34.404 37.476 27.299 27.263 8.361 8.297 8.308
rbuf-2.9 T.O. T.O. 32.966 33.062 14.629 14.355 14.320
rbuf-2.13 M.O M.O M.O M.O 20.524 20.414 21.007
rbuf-4 N.S. N.S. 77.491 78.410 28.779 29.365 10.947
rbuf-4.1 3.310 3.097 2.680 2.711 5.555 5.525 5.589
rbuf-4.3 T.O. T.O. 32.105 32.114 14.515 14.635 15.018
rbuf-4.5 M.O M.O 62.470 63.395 23.595 24.017 24.089
rbuf-4.9 M.O M.O M.O M.O 42.345 42.751 42.757
rbuf-4.13 M.O M.O M.O M.O 63.822 61.847 63.244
symbolic-counter.1.3 N.S. 1.887 1.934 1.578 5.227 3.279 1.941
symbolic-counter.1.6 N.S. 1.921 4.106 2.000 18.515 5.605 3.984
symbolic-counter.1.9 N.S. 2.333 7.850 2.560 41.552 9.185 7.411
symbolic-counter.1.12 N.S. 2.696 13.509 3.348 74.340 13.661 12.332
symbolic-counter.1.15 N.S. 3.113 21.145 4.423 114.903 19.879 18.811
symbolic-counter.2.9 N.S. 4.553 19.982 2.886 115.920 10.726 6.343
symbolic-counter.3.9 N.S. 7.067 28.489 3.208 165.473 12.316 5.285
symbolic-counter.4.9 N.S. 11.590 34.359 3.642 199.306 14.693 4.076
symbolic-counter.5.9 N.S. 18.577 36.316 4.087 205.478 16.745 3.204
symbolic-counter.6.9 N.S. 28.563 33.185 4.566 180.898 19.162 2.533
symbolic-counter.7.9 N.S. 39.224 25.803 5.139 133.851 21.238 2.014
symmetric-accumulator.1 1.834 1.840 T.O. 1.908 2.327 2.222 2.153
symmetric-accumulator.2 2.654 1.976 T.O. 2.076 2.853 2.661 T.O.
symmetric-accumulator.3 2.247 2.326 T.O. 2.231 3.451 3.116 T.O.
symmetric-accumulator.4 2.802 2.963 T.O. 2.494 3.976 3.608 T.O.
symmetric-accumulator.5 4.011 4.362 T.O. 2.707 4.599 4.122 T.O.
symmetric-accumulator.6 6.391 7.321 T.O. 3.058 5.241 4.657 T.O.
symmetric-accumulator.7 11.879 13.328 T.O. 3.443 5.930 5.268 T.O.
symmetric-accumulator.8 24.011 26.914 T.O. 3.960 6.693 5.988 T.O.
symmetric-accumulator.9 53.584 59.847 T.O. 4.484 7.541 6.695 T.O.
symmetric-accumulator.10 134.801 146.595 T.O. 5.138 8.477 7.641 T.O.
symmetric-accumulator.11 M.O M.O T.O. 5.905 9.730 8.652 T.O.
symmetric-accumulator.12 M.O M.O T.O. 6.884 10.881 9.743 T.O.
symmetric-accumulator.13 M.O M.O T.O. 7.962 12.282 11.042 T.O.
symmetric-accumulator.14 M.O M.O T.O. 9.207 13.873 12.475 T.O.
symmetric-accumulator.15 M.O M.O T.O. 10.956 16.195 14.638 T.O.
token-ring.1 N.S. 1.385 1.048 1.018 1.701 1.075 1.081
token-ring.6 N.S. 1.564 1.745 1.730 2.540 2.543 2.903
token-ring.9 N.S. 2.089 2.569 2.647 4.962 4.894 6.082
token-ring.13 N.S. 4.108 5.084 5.189 10.333 10.452 12.222
token-ring.15 N.S. 5.424 6.758 6.878 13.649 13.709 16.086
token-ring.17 N.S. 7.030 8.806 8.817 17.836 17.817 20.612
token-ring.20 N.S. 9.763 12.550 12.576 25.822 26.018 30.047
token-ring.25 N.S. 15.470 21.484 21.598 46.128 45.186 53.973
7.5. Comparing State Subsumption Matching Methods 107
the path condition is not relevant. The exact subsumption method can prove the rbuf program to
be safe without simulating a complete cycle. It is sufficient to simulate a single step. The initial
value of the bit buffer is symbolic. Rotating it once results again in a symbolic buffer which
can assume all values that the original buffer could assume. All other state parts are equal. The
rbuf benchmark performs some complex queries to the SMT solver to decide in each rotation
step that the number of high bits in the buffer is not modified. This check itself seems to have
a perceptible impact on the overall performance. The boolector solver is eventually unable to
decide the queries, for benchmarks running over multiple time steps, within the given memory
limit.
Another interesting result can be seen for the symbolic-counter example. It starts with a
symbolic counter value that is between zero and a given bound. It will count up to a specified
maximum value and then reset the counter back to its initial value2 . It has already been de-
scribed in Section 6.2.2, that once the counter is reset, the heuristic subsumption method is able
to match it with the initial state, thus terminating early. The ESS method can match even more
states in this example and thus prune the redundant state space more effectively.
The pc-sfifo-sym-1/2 benchmarks also clearly show the advantage of the ESS method. Basi-
cally a consumer and producer communicate over a shared variable. They repeat their behaviour
and increment an initially symbolic counter in each step. Essentially the heuristic methods fail
to detect the subsumption between x and x + 1, where x is a symbolic value.
The counter program is similar to the term-counter program. It also has one to four inde-
pendent threads that count up to a specified limit. But the clock thread, that notifies the counter
threads in each step, will not terminate once all threads are done but keeps sending notifications.
It is used as an example that can demonstrate the overhead involved in using a more sophisti-
cated state matching technique when a simple one is already sufficient. Event the complete
equality check is sufficient to verify this program. It can be observed that the explicit matching
methods clearly show better results for this benchmark than the solver-based methods. An-
other example where the ESM method outperforms the solver-based methods is the token-ring
benchmark.
Quite the opposite can be observed for the condition-builder benchmark. It involves the
construction of logic expressions. The program runs without time bound and eventually starts
to repeat its behaviour. The ESM method can than detect a cycle due to its logic expression
simplification rules. But it is (currently) unable to match any redundant states where the logic
expressions are reordered. The solver-based methods on the other hand can detect the equiva-
lence due to the commutativity and prune much of the redundant search space.
The symmetric-accumulator program constructs a global result value over a specified number
of time steps. In each step two dependent threads will be executed, thus both interleavings
will be considered. But both of them generate a new semantically equivalent but structurally
different symbolic expression and add it to the global result value. The simplification rules
of the ESM are currently insufficient to detect the equivalence, thus it can be observed that
the simulation time nearly doubles for each time step. The complete equality check (CEQ)
method performs similarly. The heuristic solver-based methods are able to detect the equivalent
states when the native Z3 solver is used but fail also when the boolector solver is used through
metaSMT. The exact subsumption also fails since the native Z3 solver is unable to solve the
resulting quantified formula.
either the CT or STUB algorithm can be used. The DPOR and its variants are compared against
the SPOR.
The results of some benchmarks are available in Table 7.8. All configurations use a stateful
search with the ESM method for state matching. First four configurations with SPOR are shown.
They differ in the employed persistent set algorithm, as discussed above. The configuration
shown in the fourth column computes all possible persistent sets for using the STUB algorithm
and chooses the smallest one of them. Then three DPOR variants are shown. The first is the
standard algorithm, the second manages a happens before relation and the last falls back to static
persistent sets (using the STUB algorithm) when the inference of non trivial persistent sets fails
dynamically.
Many programs yield the same results regardless which POR method is used, with regard to
the number of explored transitions. Though the DPOR algorithms finish slightly faster, since
they do not require a preliminary static analysis of the program. Some of the benchmarks show
clear differences. It can be observed that the static STUB algorithm provides the best results
compared with the other SPOR algorithms. Neither the STUB nor DPOR algorithm yield al-
ways better results than the other. The reason is that the STUB algorithm is more sophisticated,
as it also considers which transitions can enable each other, but the DPOR algorithm has more
precise informations at runtime. The best results for this benchmark set are obtained by com-
bining static and dynamic POR. The last column shows the result of DPOR which falls back to
the STUB algorithm whenever the inference of non trivial persistent sets fails.
The buffer program is an example where the DPOR excels. It first fills a buffer using multiple
threads and then checks the result. All threads write to a different memory location, thus they
are non-interfering. All different interleavings will eventually lead to the same resulting state.
Detecting array index aliasing, which means whether a[i] and a[k] can overlap, statically is a
complex problem in general, since i and k could be arbitrary (symbolic) values. The current
static analysis simply assumes that every index access to the same array can possibly overlap.
Consequently the SPOR analysis will explore all possible interleavings of the writer threads and
thus run into the state explosion problem. Though in combination with a stateful exploration,
the performance can be greatly increased. The DPOR exploration on the other hand will detect
that the threads are non-interfering and only explore a single representative path.
Quite the opposite can be observed for the safe version of the token-ring and transmitter
benchmarks. The DPOR algorithm often fails to infer non trivial persistent sets, since de-
pendencies with disabled transitions are detected. SPOR in combination with a stubborn set
algorithm performs much better, since it can still infer non trivial persistent sets in most cases.
Using a happens before relation (HBR) can indeed improve the results of the DPOR algo-
rithm, though it does not yield any improvement for most of the considered benchmarks. Since
maintaining this relation is a non trivial implementation task and can add additional runtime
overhead, it is not necessarily recommended to use this option. In the following DPOR will be
used without the HBR option.
Another interesting thing that can be observed from Table 7.8 is that always computing a
locally minimal persistent set is not always an optimal decision, e.g. the unsafe token-ring
benchmarks run better with the STUB algorithm choosing a single persistent set, than selecting
the minimal one in each step. On the other hand it can still lead to an additional reduction as the
transmitter-safe benchmark shows. Whether or not a minimal persistent set should be chosen
depends among other on the additional overhead imposed by doing so. If the number of enabled
transitions in a state is relatively small (and/or the explored states more complex, thus having
proportionally more impact on the overall performance than the persistent set computation) the
additional overhead may be negligible. In the following only a single persistent set will be
7.6. Comparing POR Implementations 111
computed.
Both the STUB and DPOR algorithms alone already yield good reductions. Their combina-
tion seems a natural choice if both algorithms have already been implemented. Based on these
results it seems promising to investigate a more sophisticated DPOR implementation, that can
compute non trivial persistent sets more often. It seems natural to employ the same idea that
lead to the STUB algorithm. Whenever a dependency to a disabled transition is detected, only
those transitions should be added, that can enable it. This set of transitions is called necessary
enabling (NES). This set can either be obtained from a static analysis or somehow inferred dy-
namically at runtime. Both methods should yield a better reduction for DPOR than is obtained
by falling back to STUB (which also pulls dependencies obtained by the static analysis and
not only necessary enabling transitions). Further investigation of this approach is left for future
research.
Result summary Neither SPOR nor DPOR is clearly superior. The combination of both of
them shows the overall best results. Thus if both configurations are available it seems reasonable
to combine them.
It can be observed that a stateful search clearly improves upon a stateless search. It sup-
ports the verification of cyclic state spaces and often is faster than the stateless DFS on acyclic
state spaces, since it avoids redundant re-explorations. As already discussed in the previous
comparison sections, the heuristic state matching methods can show significant improvements
compared to the exact method (ESS). This effect can be observed particularly well for the ESM
method. However the heuristics may fail to detect some valid subsumption, e.g. in the pc-sfifo-
sym-1/2 benchmark or symbolic-counter. The ESS configuration with SPOR is able to handle
7.7. Comparing with Kratos 113
Table 7.9.: Comparing with the state of the art tool Kratos
static POR dynamic POR combined POR
Kratos
stateful stateful stateful
Benchmark V
DFS ESMH SBC-CV ESS ESS ESMH DFS ESS ESMH
ESST
Z3
bist-cell S 2.25 2.24 2.31 2.54 2.53 1.53 1.19 1.20 2.55 2.22 1.29
buffer.p4 S N.S. 2.28 2.65 5.74 5.46 2.05 1.14 N.S. 2.60 1.70 2.67
buffer.p5 S N.S. 3.28 5.23 12.40 11.24 2.32 1.20 N.S. 2.89 1.84 5.14
buffer.p6 S N.S. 5.57 13.16 30.27 25.58 2.56 1.22 N.S. 3.29 2.00 10.61
buffer.p7 S N.S. 10.98 38.61 79.12 60.63 2.87 1.35 N.S. 3.65 2.14 23.80
buffer.p8 S N.S. 23.67 114.55 210.02 146.20 3.10 1.37 N.S. 4.00 2.22 86.87
buffer.p9 S N.S. 52.96 338.19 T.O. 342.19 3.41 1.41 N.S. 4.37 2.35 T.O.
buffer.p10 S N.S. 119.48 T.O. T.O. T.O. 3.73 1.49 N.S. 4.74 2.51 T.O.
kundu S 3.71 3.27 3.35 10.39 10.54 9.54 2.73 5.49 10.33 3.31 1.05
kundu-bug-1 U 1.42 1.46 1.43 1.57 1.56 1.20 1.02 1.03 1.57 1.44 0.71
kundu-bug-2 U 1.82 1.90 2.01 3.63 3.63 2.26 1.27 1.26 2.81 1.82 0.58
mst-bug2.1 U 4.11 4.13 4.24 4.88 4.94 2.33 1.59 1.57 4.82 4.15 1.36
mst-bug2.2 U 4.48 4.48 4.62 6.25 6.28 3.63 1.86 1.82 6.28 4.50 3.70
mst-bug2.3 U 4.85 4.94 5.23 9.15 9.26 6.54 2.27 2.18 9.16 5.14 11.08
mst-bug2.4 U 5.27 5.81 7.75 17.37 17.23 14.37 2.90 2.79 17.20 5.78 19.24
mst-bug2.5 U 6.13 7.53 18.98 45.09 42.73 39.29 4.46 3.79 41.92 7.62 37.42
mst-bug.1 U 3.89 3.84 3.81 4.47 4.49 2.25 1.53 1.54 4.49 3.81 2.41
mst-bug.2 U 4.00 4.08 4.10 5.37 5.33 2.96 1.69 1.69 5.26 4.11 9.59
mst-bug.3 U 4.26 4.24 4.22 5.30 5.31 2.81 1.67 1.66 5.26 4.19 29.77
mst-bug.4 U 4.56 4.59 4.66 7.24 7.21 4.60 1.97 1.98 7.17 4.60 87.34
mst-bug.5 U 4.75 4.93 4.94 8.29 8.16 5.54 2.16 2.16 8.27 4.91 188.09
mst.1 S 3.82 3.78 3.91 4.59 4.50 2.24 1.51 1.53 4.64 3.76 3.16
mst.2 S 3.97 3.97 4.06 5.33 5.37 2.95 1.56 1.67 5.31 4.01 12.45
mst.3 S 4.24 4.17 4.20 6.20 6.20 3.62 1.69 1.85 6.09 4.15 37.35
mst.4 S 4.44 4.41 4.39 7.02 7.00 4.42 1.75 2.00 7.06 4.38 308.52
mst.5 S 4.61 4.60 4.64 7.98 8.00 5.25 1.92 2.18 7.97 4.60 252.69
pc-sfifo-sym-1 S N.S. T.O. T.O. T.O. 1.85 1.53 T.O. N.S. 1.85 T.O. 0.46
pc-sfifo-sym-2 S N.S. T.O. T.O. T.O. 2.10 1.69 T.O. N.S. 2.15 T.O. 0.60
pres-safe.5 S 1.21 1.20 1.18 1.50 1.51 1.34 1.02 1.37 1.52 1.19 0.40
pres-safe.13 S 40.01 1.39 1.43 2.35 2.37 2.20 1.23 129.24 2.36 1.40 2.79
pres-safe.15 S 159.44 1.43 1.45 2.62 2.64 2.48 1.27 T.O. 2.58 1.51 4.58
pres-safe.20 S T.O. 1.61 1.65 3.19 3.07 3.06 1.47 T.O. 3.16 1.62 7.83
pres-safe.25 S T.O. 1.74 1.82 3.79 3.74 3.61 1.62 T.O. 3.68 1.77 17.73
pres-safe.50 S T.O. 2.47 2.50 6.65 6.48 6.55 2.38 T.O. 6.49 2.51 205.77
pres-safe.100 S T.O. 3.99 4.08 12.74 12.46 12.74 3.95 T.O. 12.67 4.05 T.O.
rbuf2-2 S N.S. N.S. 10.14 4.66 3.25 2.98 N.S. N.S. 3.28 N.S. 54.56
rbuf2-4 S N.S. N.S. 93.24 22.06 7.17 7.16 N.S. N.S. 7.17 N.S. 53.76
rbuf2-bug1.2 U 2.00 2.00 1.99 3.79 3.76 2.43 1.36 1.36 2.85 1.75 52.54
rbuf2-bug1.4 U 2.21 2.20 2.20 4.22 4.27 2.58 1.40 1.40 3.15 2.00 54.01
rbuf2-bug2.2 U 2.01 2.01 2.03 3.85 3.94 2.51 1.35 1.36 2.87 1.82 52.48
rbuf2-bug2.4 U 2.33 2.32 2.33 4.50 4.55 2.70 1.50 1.50 3.33 2.08 53.89
sfifo-1c2p.10 S N.S. 3.48 5.60 24.90 23.80 23.52 2.70 N.S. 23.70 3.53 17.90
sfifo-1c2p.20 S N.S. 5.09 16.30 54.90 45.65 44.98 4.35 N.S. 44.60 5.27 130.10
sfifo-1c2p.50 S N.S. 11.99 142.17 232.06 114.68 113.58 11.33 N.S. 113.91 12.26 T.O.
sfifo-2c1p.10 S N.S. 4.41 6.90 37.41 34.24 34.60 3.58 N.S. 35.95 4.45 18.09
sfifo-2c1p.20 S N.S. 7.00 20.26 78.14 66.45 69.87 6.26 N.S. 67.38 6.95 110.60
sfifo-2c1p.50 S N.S. 18.07 177.56 322.15 170.19 174.36 17.47 N.S. 174.29 18.09 T.O.
Continued on next page
114 7. Experiments
Table 7.10.: Comparing with the state of the art tool Kratos– continued from previous page
static POR dynamic POR combined POR
Kratos
stateful stateful stateful
Benchmark V
DFS ESMH SBC-CV ESS ESS ESMH DFS ESS ESMH
ESST
Z3
sfifo-bug-1c2p.10 U N.S. 2.81 3.03 9.17 9.23 8.46 2.02 N.S. 9.24 2.85 8.14
sfifo-bug-1c2p.20 U N.S. 3.82 5.12 17.36 16.83 15.77 3.05 N.S. 16.99 3.78 64.95
sfifo-bug-2c1p.10 U N.S. 2.96 3.21 10.40 10.24 9.37 2.17 N.S. 10.10 2.98 8.72
sfifo-bug-2c1p.20 U N.S. 4.16 5.46 19.95 18.39 17.98 3.33 N.S. 18.72 4.12 51.31
simple-pipeline S T.O. 3.19 6.48 4.82 5.88 T.O. 3.01 T.O. 5.55 3.17 7.69
sym-cnt.1.3 S N.S. 1.56 1.58 3.22 2.00 1.70 1.33 N.S. 1.98 1.59 9.24
sym-cnt.1.6 S N.S. 1.90 2.01 5.60 3.96 3.67 1.68 N.S. 3.99 2.01 41.43
sym-cnt.1.9 S N.S. 2.33 2.56 9.06 7.36 7.19 2.08 N.S. 7.28 2.35 175.43
sym-cnt.1.12 S N.S. 2.71 3.37 13.59 12.34 12.12 2.49 N.S. 12.43 2.71 T.O.
sym-cnt.1.15 S N.S. 3.03 4.37 19.46 18.71 18.56 2.88 N.S. 18.77 3.15 T.O.
sym-cnt.3.15 S N.S. 12.72 5.29 23.85 14.99 14.83 12.74 N.S. 14.79 13.10 410.08
sym-cnt.6.15 S N.S. 90.90 7.10 32.44 9.41 9.23 91.43 N.S. 9.46 91.27 T.O.
sym-cnt.9.15 S N.S. T.O. 9.62 43.16 5.23 4.94 T.O. N.S. 5.24 T.O. T.O.
token-ring-bug2.1 U 1.18 1.18 1.17 1.33 1.34 1.10 1.01 1.01 1.34 1.19 0.21
token-ring-bug2.13 U 3.71 5.19 36.72 42.93 6.95 5.42 3.47 3.05 6.93 5.19 2.61
token-ring-bug2.15 U 4.12 5.79 50.70 57.86 7.85 6.06 3.93 3.37 7.89 5.85 12.46
token-ring-bug2.17 U 4.58 6.50 70.09 75.46 8.76 6.77 4.35 3.78 8.88 6.50 M.O
token-ring-bug2.20 U 5.29 7.56 103.03 113.16 10.20 7.71 5.08 4.36 10.25 7.57 M.O
token-ring-bug.1 U 1.41 1.02 1.04 1.07 1.09 0.94 0.90 0.89 1.09 1.04 0.15
token-ring-bug.10 U 1.71 2.18 2.68 5.13 6.07 5.59 1.67 1.59 6.08 2.20 0.54
token-ring-bug.20 U 2.63 3.59 4.20 7.00 7.96 7.03 2.50 2.39 7.94 3.62 M.O
token-ring-bug.30 U 3.60 5.46 6.21 9.49 10.70 9.21 3.66 3.58 10.95 5.51 M.O
token-ring-bug.40 U 4.75 7.93 9.08 13.36 15.16 12.93 5.48 5.22 15.26 7.87 M.O
token-ring-bug.50 U 5.93 10.84 12.77 18.81 21.34 18.65 7.96 7.34 21.61 11.07 M.O
token-ring.1 S N.S. 1.01 1.05 1.07 1.11 0.95 0.88 N.S. 1.08 1.04 0.57
token-ring.6 S N.S. 1.56 1.83 2.61 2.86 8.79 2.98 N.S. 2.88 1.58 0.59
token-ring.9 S N.S. 2.07 2.57 5.04 6.04 102.67 29.51 N.S. 6.06 2.13 1.07
token-ring.13 S N.S. 5.45 5.12 10.43 12.14 T.O. M.O N.S. 12.75 4.67 5.22
token-ring.15 S N.S. 7.58 6.82 13.68 15.82 T.O. M.O N.S. 17.16 6.46 148.90
token-ring.17 S N.S. 8.40 8.82 17.81 20.57 T.O. T.O. N.S. 22.12 8.53 M.O
token-ring.20 S N.S. 9.78 12.52 25.96 29.84 T.O. T.O. N.S. 32.76 12.51 M.O
token-ring.25 S N.S. 15.48 21.56 45.66 54.26 T.O. T.O. N.S. 58.87 20.44 M.O
toy-bug-1 U 1.60 1.67 1.65 1.86 1.87 1.35 1.13 1.17 1.90 1.67 0.57
toy-bug-2 U 1.56 1.66 1.59 1.83 1.82 1.34 1.10 1.11 1.78 1.57 0.27
toy-sym S T.O. T.O. T.O. T.O. 4.57 3.88 T.O. T.O. 4.49 T.O. 1.72
transmitter.1 U N.S. 0.94 0.94 1.01 1.03 0.91 0.89 N.S. 1.01 0.94 0.14
transmitter.10 U N.S. 1.55 1.57 1.76 1.70 1.36 1.22 N.S. 1.73 1.54 0.22
transmitter.20 U N.S. 2.48 2.41 2.65 2.62 1.93 1.75 N.S. 2.65 2.47 0.29
transmitter.30 U N.S. 3.52 3.52 3.79 3.75 2.68 2.48 N.S. 3.72 3.62 0.53
transmitter.40 U N.S. 4.79 4.84 5.09 5.02 3.61 3.37 N.S. 5.13 4.89 18.25
transmitter.50 U N.S. 6.51 6.51 6.71 6.67 4.80 4.55 N.S. 6.72 6.58 3.99
transmitter.60 U N.S. 8.27 8.32 8.59 8.42 6.13 5.83 N.S. 8.62 8.30 40.76
transmitter.70 U N.S. 10.39 10.49 10.69 10.57 7.79 7.31 N.S. 10.87 10.54 2.54
transmitter.80 U N.S. 12.62 12.64 12.98 12.79 9.47 9.18 N.S. 13.05 12.86 3.61
transmitter.90 U N.S. 15.33 15.43 15.67 15.54 11.65 11.48 N.S. 15.90 15.55 M.O
transmitter.100 U N.S. 18.30 18.64 19.08 18.69 14.85 14.43 N.S. 19.73 19.21 M.O
7.7. Comparing with Kratos 115
all chosen benchmarks within the time and memory limit. Combining SPOR and DPOR further
improves the result, as already discussed in the previous section.
This combination shows very competitive results compared to Kratos. On some benchmarks
improvements of several orders of magnitude can be observed. This can especially be observed
with up-scaled benchmarks, e.g. the token-ring-bug/2, transmitter, mem-slave-tlm and pressure
benchmarks. More interestingly the same result can be observed for many safe programs with
cyclic state space, i.e. for the token-ring, symbolic-counter, buffer and simple-fifo benchmarks.
Furthermore Kratos reports a wrong result (W.R.) for the rbuf2 benchmarks. This might be
explained with the complex symbolic bitvector operations that are employed in this benchmark.
8. Conclusion
A complete symbolic simulation approach has been proposed in this thesis for the verification
of SystemC programs. This approach is built on top of SISSI [Le+13], to overcome its major
limitation in verifying assertions over cyclic state spaces. Complete symbolic simulation is a
combination of stateful model checking with symbolic simulation. The combined method al-
lows to verify safety properties in (cyclic) finite state spaces, by exhaustive exploration of all
possible inputs and process schedulings. Furthermore, stateful model checking avoids redun-
dant re-exploration of already visited states, thus it can also improve the performance when
applied to acyclic state spaces. The well-known state explosion problem is alleviated by inte-
grating two complementary reduction techniques, namely Partial Order Reduction (POR) and
State Subsumption Reduction (SSR):
• SSR can increase the effectiveness of symbolic state matching significantly. It is based on
the observation, that symbolic states represent sets of concrete states. If the set of concrete
states represented by a symbolic state s1 is a subset of concrete states represented by a
symbolic state s2 , then it is unnecessary to explore s1 , when s2 already has been explored.
The state s1 is said to be subsumed by s2 in this case. Subsumption is a generalization of
equivalence, which already is difficult to detect in the context of symbolic execution. A
basic stateful search can neither detect non-trivial equivalence nor subsumption between
symbolic states, thus missing a lot of reduction opportunities. An exact algorithm, called
ESS, has been proposed as part of SSR to detect subsumption by reducing the subsump-
tion problem to the satisfiability of a quantified SMT formula. However, due to the use of
quantifiers, this approach is computationally very expensive.
For this reason different heuristic methods have been proposed to detect subsumption
more efficiently. They can improve the scalability, by spending less time in state match-
ing, as it can often be faster to re-explore some (equivalent) part of the state space. Es-
sentially two different heuristic approaches have been proposed: the Explicit Structural
Matching (ESM) and the Solver-Based Comparison (SBC). They provide different trade-
offs between precision and runtime overhead. The ESM method attempts to structurally
match all corresponding expression pairs. It offers polynomial worst case complexity but
its precision depends on how well the expressions have been normalized in advance. The
SBC method employs an SMT solver to achieve better precision at the cost of exponential
worst case complexity. However, it still offers less runtime overhead than ESS, since no
quantifiers are used.
The experimental evaluation reflects the anticipated results. The heuristics, especially the
ESM, can result in huge improvements of the state matching efficiency. On the other hand
the exact ESS algorithm can achieve much greater state space reduction. Both approaches
can verify programs, where the other approach fails within a given time bound.
Significant scalability improvements have been observed when combining POR and SSR.
This combination enables the efficient verification of non-trivial programs. The experiments
show the competitiveness of the proposed complete symbolic simulation approach compared to
Kratos, one of the state of the art model checkers for SystemC. Improvements of several orders
of magnitude can be observed on some benchmarks. The results show that it is worthwhile
to investigate further improvements for the current approach. Some ideas are described in the
following.
Improving DPOR The experiments show that neither DPOR nor SPOR is yields always
better reductions compared to each other, despite the fact, that the DPOR algorithm has precise
runtime informations available. Based on these results it seems promising to investigate a more
sophisticated DPOR implementation, that can compute non trivial persistent sets more often.
It seems natural to employ the same idea that lead to the stubborn set (STUB) algorithm, that
is used to compute static persistent sets. Whenever a dependency to a disabled transition is
detected, only those transitions should be added, that can enable it. This set of transitions is
called necessary enabling (NES). This set can either be obtained from a static analysis, which is
more lightweight than a complete dependency analysis required for SPOR, or somehow inferred
dynamically at runtime, similarly to the dependency relation. Both methods should yield a
better reduction for DPOR than is obtained by falling back to STUB, which will also pull
dependencies obtained by the static analysis and not only the necessary enabling transitions.
Further investigation of this approach is left for future research.
Combining different state subsumption methods Based on the experiment results and
observations it seems quite natural to combine different state subsumption methods. An idea
would be to apply multiple state matching methods in parallel, e.g. start the ESM, the heuristic
and exact subsumption checks and report a state match if either of these methods does match and
else report that the considered states do not match. This might be especially useful on a multi-
core machine. Since all these checks are read-only, they will not run into any synchronization
issues. The difficulty here seems to be the cleanup. Whenever a method reports a positive result,
the others should be terminated. Currently the SMT solver do not provide a reliable API to do
so. A solution would be to fork new independent processes that do the actual work but that
seems to much of an overhead.
Another useful direction that can be further investigated might be to apply the ESM first
and only if it reports a negative result (no state match), than apply a solver-based method.
The implementation is quite trivial, but some preliminary results obtained by doing so show
no significant improvement compared to running the solver-based method alone. The reason
seems to be that negative results are reported (much) more often than positive results. Thus the
solver-based method is still called very often. The benefits obtained from some reduced calls to
the solver are negated by the additional calls due to the ESM method. Its left for future research
to investigate how to combine these methods effectively.
The symmetric accumulator benchmark is a good example that shows the exact subsumption
check can be more performance intensive, than the heuristic checks. The exact subsumption is
unable to infer that some states have already been visited. The checks are too complex and thus
the time limit is exhausted. These experiments suggest that it might be useful to set a maximum
time limit for symbolic state matchings in solver-based methods. Doing so could still detect
more equivalent states than the ESM method, while not suffering too much from unsolvable
instances, especially in acyclic state spaces.
This observation directly leads to another promising idea. The state matching method can
be chosen dynamically at runtime for each pair of states (when their hash value and concrete
state parts already match) s and v, where s is the currently explored state and v the already
visited one. The rational behind this idea is that some state matchings are more important than
others. States on a cycle are important. Failing to detect the cycle leads to non termination of
the simulation. On the other hand if s and v do not form a cycle, s can be re-explored again.
Detecting that s and v match is merely an optimization in this case.
A simple necessary condition for s and v to form a cycle is that v currently is in the search
stack. So in this case a solver-based matching could be employed. Still it might be useful to
120 8. Conclusion
start with a simple matching method and gradually increase the complexity. If s and v they
really form a cycle, the states will be revisited until the cycle is detected (or the time/memory
resources allocated for the simulation are exhausted). An idea based on this observation would
be to keep a counter for each state on the stack, how often it has been (partially - considering
the hash value and concrete state parts) matched. Based on the current value of the counter, a
more sophisticated state matching algorithm can be employed or the time limit can be gradually
increased.
If v is not on the search stack, it cannot form a cycle with s. So either a lightweight matching
method, like the ESM, or a solver-based method with a relatively small timeout should be used.
The timeout can either be a fixed value or itself dynamically chosen based on the expected
time necessary to re-explore the state space of the already visited state. A simple method to
approximate this time would be to store a timestamp for each state added to the stack. Whenever
a state is backtracked another timestamp is recorded and the state is moved into the set of visited
states1 . The expected re-exploration time for the state is then the difference between the two
timestamps.
1 Or into a possibly size limited cache, as proposed in another point for future work.
A. Appendix
A.1.1. Definitions
Indexing a set S by a number n, written as S[n], returns any arbitrary element in S. The only
requirement is, that the operation is deterministic, that is S[n] = S[k] if n = k. Sometimes a set
will be converted to a tuple, to make the indexing operation more explicit. Tuples will either be
written in simple parentheses or brackets, e.g. (1, 2) or [1, 2]. The auxiliary function zip takes
two tuples A and B and yields a set S of pairs from A and B. It is defined as:
For example zip([1, 2], [a, b, c]) = {(1, a), (2, b)}. The operation Γ (X) returns all symbolic liter-
als reachable from X. It is defined for (symbolic) values, e.g. Γ (x1 + 2 ∗ x2 ) = {x1 , x2 }, and also
for equality assumptions m as {e | (e = a ∨ e = b) ∧ (a, b) ∈ m}, e.g. Γ ({(x1 , x2 ), (x3 , y1 )}) =
8 return ans
Algorithm 17: Collect all disjunct reachable symbolic literals from two states
Input: Structurally compatible states s1 and s2
Output: Pair of disjunct symbolic literal sets A and B reachable from s1 and s2 respectively
1 A ← {}
2 B ← {}
3 for v1 , v2 ∈ symbolicPairs(s1 , s2 ) do
4 A ← A ∪ Γ (v1 )
5 B ← B ∪ Γ (v2 )
6 C ← A∩B
7 A ← A \C
8 B ← B \C
Algorithm 18: Generate all sets of consistent equality assumptions between A and B.
Input: Disjunct sets A and B of symbolic literals
Output: Set of consistent equality assumption sets
1 if |A| > |B| then
/* swap A and B, this ensures |A| ≤ |B| */
2 A, B ← B, A
3 M ← {zip(tuple(A), P) | P ∈ PermutationsWithMixedRepetition(B, {ε}, |A|)}
4 return {m | m ∈ M ∧ m = {(a, b) ∈ m | a = ε ∧ b = ε ∧ type(a) = type(b)} ∧ m = 0}
/
2 function generatePreprocessedConstraints(s1 , s2 ) is
3 ans ← []
4 for v1 , v2 ∈ symbolicPairs(s1 , s2 ) do
5 A ← Γ (v1 )
6 B ← Γ (v2 )
7 cs ← separateLiteralsBy((A, B), λ x : type(x))
8 for c ∈ cs do
9 for e ∈ separateEqualLiterals(c) do
10 ans ← ans · e
11 return ans
14 function separateEqualLiterals(c:(A,B)) is
15 ans ← {}
16 common ← A ∩ B
17 for e ∈ common do
18 ans ← ans · ({e}, {e})
every symbolic literal a ∈ A can be mapped upon any symbolic literal b ∈ B or left unmapped.
The second part is available in Algorithm 18. It shows how to generate all possible consistent
equality assumptions from A and B. First it is ensured that |A| ≤ |B|. Then all possible equality
assumptions are generated in Line 3. Lastly all unmapped or type incompatible (to ensure
consistency) pairs are filtered out of the set of equality assumptions resulting in all possible
consistent equality assumption sets.
In the following, some remarks about the worst case complexity of this algorithm are pre-
sented. Assuming w.l.o.g. |A| ≤ |B|, every x ∈ A can be mapped upon a different y ∈ B or
can be left unmapped. So the first element x1 ∈ A can be associated with any element in B or
left unmapped. The next x2 ∈ A has one less choice if x1 has been mapped, else it will have
the same amount of choices as x1 , and so on. Thus an upper bound for the number of gener-
ated mappings (equality assumption sets) is nk . Where n is max(len(A), len(B)) + 1 and k is
min(len(A), len(B)). A more precise upper bound is the number p of permutations returned
from the function call PermutationsWithMixedRepetition(B, {ε}, |A|) in line Line 3. If all sym-
bolic literals in A and B are type combinations, that is ∀a, b ∈ A ∪ B : type(a) = type(b), then p
is also a lower bound, since no mappings will be filtered out in Line 4.
The number of possible mappings grows very fast with the number of disjunct symbolic lit-
erals in s1 and s2 . This algorithm is impractical for all but small such numbers. Additional filter
heuristic, that discard mappings early that unlikely will lead to the detection of subsumption
124 A. Appendix
between states, are necessary, to make the algorithm scalable. An example heuristic would be
to require that every a ∈ A is mapped upon a b ∈ B (assuming w.l.o.g. that |A| ≤ |B|). This
change can be easily integrated into the existing algorithm by changing the function call Per-
mutationsWithMixedRepetition(B, {ε}, |A|) into PermutationsWithoutRepetition(B, |A|). Then
n!
an upper bound for the number of possible mappings would be (n−k)! . If all symbolic literals in
n!
A and B are type compatible, then it would be exactly (n−k)! .
Preprocessing Phase
The preprocessing phase takes two (structurally compatible) execution states s1 and s2 and
generates a set of (preprocessed) constraints. The method is shown in Algorithm 19. The
generated result set is denoted ans and is initially empty. Every symbolic state pair (v1 , v2 )
between s1 and s2 is considered one after another. Each of them is transformed into an initial
constraint c = (A, B), where A and B are assigned all reachable symbolic literals from v1 and v2
respectively2 . Next, in Line 7, the literals in A and B are separated by type. This operation yields
a set of constraints cs, where ∀c = (A, B) ∈ cs : (∀a, b ∈ A ∪ B : type(a) = type(b)). Then every
constraint c ∈ cs is further separated by splitting equal symbolic literals, yielding constraints
(A, B) where additionally A ∩ B = 0/ iff A = B ∧ |A| = 1 holds. The resulting constraints are
added to the preprocessed constraint set ans.
The following example shall illustrate the concepts of the presented algorithm. Given two
states s1 and s2 with s1 ∼ s2 and symbolic state parts:
Further it is assumed that x5 and y5 have type T1 . All other symbolic literals have the type T2 ,
with T1 = T2 .
Initially the result set ans is empty. Both symbolic state parts have three entries, that can be
grouped to pairs: the path conditions and the variable values v and w. First the pair (pc(s1 ),
pc(s2 )) is considered. Its initial constraint c = (A, B) is computed as A = Γ (pc(s1 )) = {x1 , x3 }
and B = Γ (pc(s2 )) = {y1 , y4 } in line 5 and 6. Since A and B neither contain any type incom-
patible nor equal literals, the constraint c is added to ans without further separation. Next the
pair (vars(s1 )[v], vars(s2 )[v]) is considered. In this case A = {x1 , x2 , x5 } and B = {y1 , y2 , y5 }.
This constraint is separated by type into cs={({x1 , x2 }, {y1 , y2 }), ({x5 }, {y5 })} in line Line 7,
since x5 and y5 have type T1 and x1 , x2 , y1 , y2 have a different type T2 . Neither of these two
constraints ci = (Ai , Bi ) is further separated in Line 9, since they don’t contain any common
symbolic literals, that is Ai ∩ Bi = 0. / Both constrains are added to ans in Line 10. Lastly the pair
(vars(s1 )[w], vars(s2 )[w]) is considered. In this case A = {x3 , x4 , x6 } and B = {y3 , y4 , x6 }. This
constraint is not separated by type, so cs = {(A, B)} in Line 7. But it is separated by identity in
Line 9 into two constraints ({x3 , x4 }, {y3 , y4 }) and ({x6 }, {x6 }), since A ∩ B = {x6 }. Both are
added to ans. The result of the preprocessing phase is then: ans = {({x1 , x3 }, {y1 , y4 }), ({x1 , x2 },
{y1 , y2 }), ({x5 }, {y5 }), ({x3 , x4 }, {y3 , y4 }), ({x6 }, {x6 })}.
Construction Phase
This section presents an algorithm, the construction phase, that takes a set of preprocessed
constraints and transforms them into a set cs of simplified constraints. The algorithm is shown
in Algorithm 20. The constraint set cs is simplified, if every pair of constraints a, b ∈ cs is
disjunct. A formal definition of this property is given in Line 27.
A constraint is just a pair of sets, so common set operations can be naturally extended to work
with constraints. Doing so allows to express the following algorithm more naturally. Given two
constraints c1 = (A1 , B1 ) and c2 = (A2 , B2 ):
c1 ∩ c2 = (A1 ∩ A2 , B1 ∩ B2 )
c1 ∪ c2 = (A1 ∪ A2 , B1 ∪ B2 )
c1 \ c2 = (A1 \ A2 , B1 \ B2 )
c1 = 0/ ⇐⇒ A1 = 0/ ∧ B1 = 0/
c1 = c2 ⇐⇒ (A1 = A2 ) ∧ (B1 = B2 )
size(c1 ) = (|A1 |, |B1 |)
swap(c1 ) = (B1 , A1 )
The construction phase starts with an empty result set cs. Each c ∈ input is analyzed one after
another using the function tryAddConstraint. This function expects that c is preprocessed (this
property holds by definition of the preprocessing algorithm) and preserves the invariant that cs
is simplified.
It starts by executing a loop where each constraint e currently in cs will be compared with c.
Inside the loop first the common part a = c ∩ e of c and e will be computed. If a is empty or
equals e, then e will be preserved (the if statement body in Line 9 will not be entered). Else,
neither e and c are disjunct, nor e is fully contained in c, the constraint e will be separated into
the common part a and the rest b = e \ c. So e is removed from cs and both a and b are added
to cs. Doing so keeps cs simplified, since a ∪ b = e and e is disjunct to all other constraints in
cs. Thus a and b are also disjunct to all other constraints in cs (which is the reason why they
126 A. Appendix
2 for c ∈ input do
3 tryAddConstraint(c)
4 @invariant: isSimplified(cs)
5 @require: isPreprocessedConstraint(c)
6 procedure tryAddConstraint(c : (A, B), first step=True) is
7 for e ∈ cs do
8 a ← e∩c
9 if (a = 0)
/ ∧ (a = e) then
10 cs ← cs \ e
11 addNewConstraint(a)
12 b ← e\c
13 assert(b = 0)
/
14 addNewConstraint(b)
15 c ← c\e
16 if (c = 0)
/ then
17 return
22 @require: c ∈/ cs
23 procedure addNewConstraint(c=(A,B)) is
24 cs ← cs · c
25 predicate isPreprocessedConstraint(c:(A,B)) is
26 return [(A ∩ B = 0)
/ ⇐⇒ (A = B ∧ |A| = 1)] ∧ (∀a, b ∈ A ∪ B : type(a) = type(b))
27 predicate isSimplified(cs) is
28 return ¬ hasConstraintsWithCommonLiterals(cs)
29 predicate hasConstraintsWithCommonLiterals(cs) is
30 return ∃(c1 , c2 ) ∈ 2-combinations(cs) : ¬(c1 ∩ c2 = 0)
/ ∨ ¬(swap(c1 ) ∩ c2 = 0)
/
31 function 2-combinations(cs) is
32 return {{A, B} | A ∈ cs ∧ B ∈ cs}
A.1. Generating Consistent Equality Assumptions 127
are added to cs without further analysis, since they are disjunct to all other constraints, they will
not lead to any further separation).
Then in Line 15, c will be simplified to c ← c \ e. This is only an optimization that can make
it unnecessary to traverse the whole current constraint list cs twice (since the function will call
itself again in Line 19) every time a constraint is added. Doing so will not miss any separation of
other constraints in cs, because cs is simplified, so common parts of c with e cannot be common
parts with any other constraint x ∈ cs where x = e. If c becomes empty, the function will return.
After the loop it is checked, whether this function has been called the first time for the cur-
rently analyzed constraint c. If it has been called the first time, then it will be called again with
c being swapped. The reason is that it is also necessary to separate constraints e = (A2 , B2 ) with
c = (A1 , B1 ) where A1 ∩ B2 = 0/ or B1 ∩ A2 = 0./ Else the function is called the second time.
In this case c is not empty (else the analysis would have been already aborted in Line 17 or c
would violate the property that it is preprocessed) and has no common parts with any constraint
currently in cs (all common parts have already been separated). So c is added to cs. Doing so
keeps cs simplified.
Remark. The above algorithm starts with an empty set of constraints cs = {}. This one is
simplified by definition. Every constraint that is added to cs, this only happens in Line 24 and
is initiated from the lines {11, 14, 21}, preserves the property, that cs is simplified. Thus cs is
simplified when the algorithm finishes.
In the following, the construction phase algorithm will be applied to a small example for
illustration. Given the set of preprocessed constraints (these are the result of the example used
to illustrate the preprocess phase in the previous section):
input = {({x1 , x3 }, {y1 , y4 }), ({x1 , x2 }, {y1 , y2 }), ({x5 }, {y5 }), ({x3 , x4 }, {y3 , y4 }), ({x6 }, {x6 })}
The construction phase starts with an empty result set cs. Each c ∈ input is considered one
after another. Since there are five c ∈ input, there will be five steps. The stepwise constructed
result is available in Table A.1. The table shows the current step, the considered constraint
c ∈ input and the current result after analyzing c. First c1 =({x1 , x3 }, {y1 , y4 }) is directly added
to cs, since cs is empty at the beginning. Next c2 =({x1 , x2 }, {y1 , y2 }) is considered. Since
c1 = c2 and a = c1 ∩ c2 =({x1 }, {y1 }) is not empty (in Line 9), c1 is removed from cs and the
separated constraints a and b = c1 \ c2 =({x3 }, {y4 }) are added instead. Then c2 is simplified to
c2 ← c2 \c1 =({x2 }, {y2 }) in Line 15. Further separations do not occur, so c2 is eventually added
to cs (actually swap(c2 ) but it doesn’t matter, since constraints are symmetric anyway). In step 3
128 A. Appendix
the constraint c3 =({x5 }, {y5 }) is considered. Since neither c3 = (A, B) nor swap(c3 ) = (B, A)
does overlap with any existing constraint currently available in cs, it is directly added to cs.
Then in step 4 the constraint c4 =({x3 , x4 }, {y3 , y4 }) is considered. This one overlaps with the
existing constraint ({x3 }, {y4 }) so it is separated into ({x3 }, {y4 }) and ({x4 }, {y3 }). The latter
constraint is added to cs (the former is not, since it is already available). Finally in step 5 the
constraint ({x6 }, {x6 }) is added to cs, similar to step 3, without any further separation.
Remark. Constraints of the form (A1 , B1 ) with A1 = B1 are not filtered out prior the construction
phase, because they can lead to the separation of constraints (A2 , B2 ) if either A1 ∩ A2 , A1 ∩ B2 ,
B1 ∩ A2 or B1 ∩ B2 is not empty.
Generation Phase
Once a simplified set of constraints cs has been constructed, it can be used to generate sets of
(simplified) consistent equality assumptions, as shown in Algorithm 21. Basically each c =
(A, B) ∈ cs is transformed into a set of locally (only for this single constraint) complete literal
mappings. A globally (for all constraints in cs) complete literal mapping is obtained by choosing
a mapping from each of the above local sets. Since cs is simplified, all of its constraints are
disjunct, thus every such constructed mapping is consistent.
The following concrete example will be used to illustrate the algorithm, by showing the rele-
vant intermediate results together with the end result. The following set of simplified constraints
is given as input:
(a1 , a2 ) - (b1 , b2 )
(a3 , a4 ) - (b3 , b4 )
(a5 ) - (b5 )
Thus cs[1] = ({a1 , a2 }, {b1 , b2 }), and cs[2] = ({a3 , a4 }, {b3 , b4 }), and cs[3] = ({a5 }, {b5 }),
and cs = {cs[1], cs[2], cs[3]}. Each constraint c ∈ cs is transformed into a set of (local) equality
assumption sets in Line 8. The result of each transformation is denoted as M. Since three
constraints cs[i] are available, three corresponding sets Mi will be produced, with:
M1 = {{(a1 , b1 ), (a2 , b2 )}, {(a2 , b1 ), (a1 , b2 )}}
M2 = {{(a3 , b3 ), (a4 , b4 )}, {(a4 , b3 ), (a3 , b4 )}}
M3 = {{(a5 , b5 )}}
Neither of them is empty, thus they are all collected as choices = [M1 , M2 , M3 ]. The next step
is to combine all ei ∈ Mi with each other. This happens by computing the cartesian product of
[M1 , M2 , M3 ] in Line 11, which results in:
product([M1 , M2 , M3 ]) = {({(a1 , b1 ), (a2 , b2 )}, {(a3 , b3 ), (a4 , b4 )}, {(a5 , b5 )}),
({(a1 , b1 ), (a2 , b2 )}, {(a4 , b3 ), (a3 , b4 )}, {(a5 , b5 )}),
({(a2 , b1 ), (a1 , b2 )}, {(a3 , b3 ), (a4 , b4 )}, {(a5 , b5 )}),
({(a2 , b1 ), (a1 , b2 )}, {(a4 , b3 ), (a3 , b4 )}, {(a5 , b5 )})}
The last step is to flatten all inner sets into single top level sets, which results in a set of four
different consistent equality assumption sets:
f latten(product([M1 , M2 , M3 ])) = {{(a1 , b1 ), (a2 , b2 ), (a3 , b3 ), (a4 , b4 ), (a5 , b5 )},
{(a1 , b1 ), (a2 , b2 ), (a4 , b3 ), (a3 , b4 ), (a5 , b5 )},
{(a2 , b1 ), (a1 , b2 ), (a3 , b3 ), (a4 , b4 ), (a5 , b5 )},
{(a2 , b1 ), (a1 , b2 ), (a4 , b3 ), (a3 , b4 ), (a5 , b5 )}}
A.1. Generating Consistent Equality Assumptions 129
3 function flatten(S) is
4 return { e∈X e | X ∈ S}
11 return flatten(product(choices))
The number of equality assumption sets that will be generated by the above algorithm is:
|cs | ni !
∏i=1 (ni −k i )!
with cs = {(A, B) ∈ cs | A = 0/ ∧ B = 0}
/ and (A, B) = cs [i] and ni = max(|A|, |B|)
i!
and ki = abs(|A| − |B|). The inner term (nin−k i )!
is due to the permutations in Line 8. The outer
product corresponds to the cartesian product in Line 11. Filtering out constraints that will not
generate any equality assumptions happens in Line 9.
The number of equality assumption sets generated by this method does no longer grow ex-
ponentially with the number of all disjunct symbolic literals, but with the size of the maximum
constraint c = (as, bs) ∈ cs, which is defined as max(|as|, |bs|). An upper bound for this maxi-
mum size is the maximum number of disjunct symbolic literals for each linked symbolic pair:
max({max(|Γ (v1 ) \ Γ (v2 )|, |Γ (v2 ) \ Γ (v1 )|) | (v1 , v2 ) ∈ symbolicPairs(s1 , s2 )})
The preprocessing phase starts with such maximal constraints for each pair (v1 , v2 ), which is
in symbolicPairs(s1 , s2 ). These constraints can only be separated into smaller ones (using
simplification heuristics), but they will never grow.
So this algorithm to generate equality assumptions has still an exponential worst case com-
plexity. But in practice it might often perform (considerably) better.
Example 4. The example from the preprocessing and construction phase started with the sym-
bolic state parts:
{({x1 }, {y1 }), ({x3 }, {y4 }), ({x2 }, {y2 }), ({x5 }, {y5 }), ({x4 }, {y3 }), ({x6 }, {x6 })}
The generation phase would then yield only a single set of equality assumptions, namely
{(x1 , y1 ), (x3 , y4 ), (x2 , y2 ), (x5 , y5 ), (x4 , y3 )}. This one is already sufficient to show that s1
130 A. Appendix
and s2 are equivalent. And it even seems to be necessary (other consistent equality assumptions
would not lead to the detection of s1 and s2 being equivalent). The first algorithm (presented
in Section A.1.2) on the other hand would generate either 1546 or (5−5)!5!
= 5! = 120 sets of
equality assumptions, depending whether all- or only (locally) complete- sets are generated. So
the second algorithm can lead to significant performance improvements.
Additional Heuristics
The preprocessing phase of the algorithm separates all initial constraints c = (A, B) by type. This
is required so that the generation phase will yield type-compatible equality assumptions, which
is a necessary condition for them to be consistent. Additional separation criteria can be used
to generated smaller constraints. It seems reasonable to separate constraints by source(-file)
location of the symbolic literals. Doing so can often yield (considerably) smaller constraints but
should still be sufficient to detect all state cycles (since the same statements would be executed
for each cycle, therefore all introduced symbolic literals should have the same source locations
as the ones introduced on the previous cycle execution). Two literals that have the same source
location necessarily have the same type, thus it preserves type-compatibility. So separation by
location can be used as drop-in replacement to type based separation (the key function λ x :
type(x) would just be replaced by λ x : location(x) in Line 7 of the preprocessing Algorithm
19). It also implies that constraints separated by location cannot be larger than those separated
by type.
The construction phase accepts any new constraint c = (A, B) into the collected constraint set
cs so long as cs stays simplified by adding c. This means, by definition of simplified, that neither
two different constraints in cs have any common symbolic literals. Simplification is ensured, by
comparing the new about to be added constraint with all existing constraints, further separating
them if necessary. Ultimately all these separated constraints will be accepted, since they keep
the constraint set cs simplified.
Additional requirements can be defined to decide whether a new constraint is accepted or
rejected. When a constraint is rejected, the whole algorithm aborts and the compared states s1
and s2 are considered to be non-equivalent. The idea is to require that |A| = |B| holds, as it
seems rather uncommon that constraints with |A| = |B| will lead to equality assumptions that
in turn lead to the detection of equivalence (or more generally state subsumption). Because if
|A| = |B| then at least one symbolic literal will be left unmapped (actually abs(|A| − |B|)). So
basically If |A| = |B|, than the constraint c = (A, B) is called regular, else irregular. The special
case where abs(()|A| − |B|) ≤ 1 will be called almost regular.
The following example may illustrate the concept more clearly. Given two symbolic state
parts ζ (s1 )=(pc: T , a: x1 ∗ x2 + x1 , b: x3 ) and ζ (s2 )=(pc: T , a: y1 ∗ (y2 + 1), b: y1 ). Further
assuming that all symbolic literals x1 ,x2 ,x3 ,y1 ,y2 have the same type and constraints are sepa-
rated by type, the preprocessing phase will yield the two constraints c1 = ({x1 , x2 }, {y1 , y2 })
and c2 = ({x3 }, {y1 }). The construction phase starts with an empty constraint set cs. In the
first step c1 is added to cs. Next c2 is considered. Since c1 ∩ c2 = ({}, {b1 }) is not empty, both
constraints will be further separated, resulting in cs={({a1 , a2 }, {b2 }), ({}, {b1 }), ({a3 }, {})}
If regular constraints are required, then the construction phase and with it the whole algorithm
would abort without generating any equality assumptions. The states s1 and s2 would be con-
sidered non-equivalent without calling the solver at all. Else two different simplified consistent
equality assumption sets, namely m1 ={(a1 , b1 )} and m2 ={(a2 , b1 )} will be generated. So the
solver would be called twice to detect, that s1 and s2 are non-equivalent.
Requiring regular constraints is additional heuristic, as for example ζ (s1 )=(pc: T , a: 5) and
A.2. POR Correctness Proof: Deadlock Preserving 131
ζ (s2 )=(pc: x1 = 5, a: x1 ) would not be detected as being equivalent, since the only generated
constraint ({}, {x1 }) is not regular. Though many such cases, including this one, could already
be simplified in advance, e.g. replacing x1 with 5 since x1 = 5 ∈ pc(s2 ) anyway. Or the require-
ment that constraints must be regular could be relaxed to requiring almost regular constraints
(or more generally abs(|A| − |B|) ≤ k).
Remark. Preprocessing and the subsequent construction don’t have to be completely separate
phases. They can easily be intertwined by just directly calling tryAddConstraint from Algorithm
19 in Line 10, instead of collecting the constraint. Doing so can be useful, if heuristics are
employed, that allow to abort early, like requiring regular constraints does. This can often
make it unnecessary to generate all preprocessed constraints, when only some of them would
be sufficient to abort.
Remark. Other algorithms can be used to generate equality assumptions m which can than be
used to detect subsumption between states. The only requirement is that m is consistent (type-
compatible and unambiguous).
Remarks
As already stated, this second algorithm generates only a subset of all possible equality as-
sumptions. It filters out those, that will only unlikely lead to the detection of state subsump-
tions. More precisely the algorithm generates equality assumptions that satisfy the following
definition.
Definition 29
Let m be a set of equality assumptions for the states s1 and s2 , m is called simplified iff the
following properties C1 and C2 hold (with P = symbolicPairs(s1 , s2 ))
C1 : ∀(v1 , v2 ) ∈ P : (∀a ∈ Γ (v1 ) : a ∈ Γ (m)) ∨ (∀b ∈ Γ (v2 ) : b ∈ Γ (m))
C2 : ¬∃(a, b) ∈ m : (∃(v1 , v2 ) ∈ P : (x, y ∈ {a, b} ∧ ¬(x ∈ Γ (v1 ) ⇐⇒ y ∈ Γ (v2 ))))
A set of equality assumptions that satisfies C1 will be called (locally) complete.
Basically C1 states that for every symbolic state pair (v1 , v2 ) either all symbolic literals from
v1 have been mapped or all symbolic literals from v2 have been mapped. If only regular con-
straints are accepted in the construction phase, then every symbolic literal of v1 and v2 is mapped
(there exist no unmapped literal).
The property C2 states that every equality assumption (a, b) ∈ m must appear in corresponding
symbolic state pairs. So for every symbolic state pair (v1 , v2 ) if a appears in v1 (or v2 ) then b
must appear in v2 (or respectively v1 ).
A similar proof has already been provided in [God96]. The proof in this section is slightly
differently formulated and adapted to the terminology used in this thesis. It is provided here to
serve as an introduction to more advanced proofs.
First the overall proof method is outlined. The idea is to show that for every state s in AR and
every trace w that leads to a deadlock in AG from s, there exists an equivalent trace wi ∈ [w]s
that leads to a deadlock in AR from s. The Definition 6 of equivalent traces implies that w and
all wi have the same length. The proof proceeds by induction. The induction step requires some
auxiliary lemmata which are introduced in the following. Basically it is shown that at least one
ti , which is the first transition of wi , is in the persistent set of s. Thus some progress is made
toward the deadlock in AR . Together with the induction hypothesis the theorem can be finally
proved. In the following first the auxiliary lemmata are introduced then the main theorem will
be proved as outlined above.
Proof. The proof proceeds by contradiction. Assume that neither ai is in T . Let w = t1 ..tn and
t1 t2 tn
let s = s1 → s2 → ...sn → sn+1 = sd be the sequence of states w goes through in AG . By Theorem
A.2.2 at least one transition of w is in T. Let tk be the first such transition (k ≤ j for all t j of w
that are in T ). According to Definition 10 of persistent sets, tk is independent with all t j in s j
for j ∈ {1..k − 1}. Thus by Definition 6 of trace equivalence, the traces w = tkt1 ..tk−1tk+1 ..tn
and w = t1 ..tn are equivalent, so w ∈ [w]s 3 . Since tk is the first transition of w and tk is in T , the
proof is completed.
Theorem A.2.4
Let s be a state in a reduced STS AR , where the reduction function r satisfies the conditions
C0 and C1 as defined in Section 3.1.1. Let sd be a deadlock reachable from s in AG by a
trace w. Then sd is also reachable from s in AR .
Proof. The proof proceeds by induction over the length of the trace w.
IB : |w| = 0. This case immediately holds, since executing an empty trace does not change
the state (thus s = sd ).
3 And analogously w ∈ [w ]s but this result is irrelevant for the proof.
A.3. POR Correctness Proof: Assertion Violations Preserving 133
IS : |w| = n + 1
Since a non empty trace is executable from s in AG , enabled(s) cannot be empty. Con-
sequently r(s) is not empty due to condition C0 . Condition C1 ensures that r(s) is a
persistent set in s.
Thus, according to Theorem A.2.3 there exists a trace wi in AG such that wi ∈ [w]s and
the first transition ti in wi is in r(s), hence ti is also explored in AR . Executing ti from s
results in a state s in AR (since ti is explored in AR from s). Since wi ∈ [w]s , executing
either wi or w from s will result in the same state, according to Definition 2.6.1 of trace
equivalence. Thus the deadlock state sd is reached from s when executing wi in AG , i.e.
wi
s −→ sd . Let w be the rest of wi (all transitions except ti ).
So s is a state in AR , whose reduction function r by assumption satisfies the conditions
C0 and C1 , and the deadlock sd is reachable from s in AG by the trace w . Consequently,
by induction hypothesis (IH) the deadlock sd is also reachable from s in AR .
From the above theorem it follows immediately that Theorem A.2.1 holds, since the initial
state s0 is in AR . Thus the reduced state space preserves all deadlocks of the corresponding
complete state space AG .
The expression Pre f ([w]s ) denotes the set of prefixes of the sequences in [w]s for every trace
w and state s (thus [w]s ⊆ Pre f ([w]s ) always is valid), as described in Section 3.1.3.
According to [EP10] it can be shown, though no proof is available there, that a reduced state
space AR , corresponding to a complete state space AG , where the reduction function r satisfies
the conditions C0 , C1 and C2 as defined in Section 3.1, is a trace automaton of AG . In fact
conditions C1 and C2 alone are already sufficient, since C2 , in combination with C1 , already
implies C0 . Trace automata have been introduced in [God91]. They preserve safety properties,
e.g. specified in form of assertions, of the complete state space. In [God96] they are defined as:
Definition 30 (Trace Automaton)
Let AG be the complete state space of a system. A reduced state space AR for this system is
a trace automaton for this system if, for all sequences w of transitions from the initial state
s0 in AG , there exists a sequence w of transitions from s0 in AR such that w is a linearization
of a trace defined by an extension of w, i.e. w ∈ Pre f ([w ]s0 ).
The definition of trace automata directly coincides with the above main Theorem A.3.1. Con-
sequently proving that theorem will also prove that AR is a trace automaton of AG , which means
it will preserve all assertion violations of AG .
134 A. Appendix
Lemma A.3.2. Let s be a state in AR . Let w = t1 ..tn be a trace in AG leading to an error state
from s. Let T be a persistent set in s. If ti ∈
/ T for all i ∈ {1..n}, then ∀ai ∈ T : w ∈ Pre f ([ai w]s ),
else let tk be the smallest ti ∈ T , w ∈ Pre f ([tk w ]s ) where w = t1 ..tk−1tk+1 ..tn is a trace in AG .
Proof. The proof will consider both cases, one where the if condition is true and one where it
t1 t2 tn
is false. Let s = s1 → s2 → ...sn → sn+1 be the states that w goes through in AG .
Case : ∀i ∈ {1..n} : ti ∈
/ T.
According to the Definition 10 of persistent sets, all transitions ai ∈ T are independent
with t j in s j for all j ∈ {1..n}. Consequently ai w ∈ [wai ]s for all ai ∈ T . Thus it im-
mediately follows that ai w ∈ Pre f ([wai ]s ) since [wai ]s ⊆ Pre f ([wai ]s ) by definition of
Pref.
Case : ∃i ∈ {1..n} : ti ∈ T .
Let tk be the first ti ∈ T (∀ti ∈ T : k ≤ i). Then by Definition 10 of persistent sets, tk is in-
dependent with all t j in s j for j ∈ {1..k − 1}. Consequently tkt1 ..tk−1 ∈ [t1 ..tk−1tk ]s , which
directly implies tkt1 ..tk−1tk+1 ..tn ∈ [t1 ..tk−1tktk+1 ..tn ]s since both traces are extended in the
same way. Analogously to the above case it immediately follows that tkt1 ..tk−1tk+1 ..tn ∈
Pre f ([tk w ]s ) where w = t1 ..tk−1tk+1 ..tn .
Lemma A.3.3. Let s be a state in AR . Let w = t1 ..tn be a trace in AG leading to an error state
tw
from s. Let T be a persistent set in s. If ti ∈
/ T for all i ∈ {1..n}, then ∀t ∈ T : s −→ is also an
t w
error state in AG , else let tk be the smallest ti ∈ T , let w = t1 ..tk−1tk+1 ..tn then s −−
k
→ is also an
error state in AG .
Proof. The proof will consider both cases, one where the if condition is true and one where it
is false.
Case : ∀i ∈ {1..n} : ti ∈
/ T.
According to Lemma A.3.2 w ∈ Pre f ([ai w]s ) for all ai ∈ T . Since w leads to an error
state from s in AG , the trace ai w will also lead to an error state from s in AG .
Case : ∃i ∈ {1..n} : ti ∈ T .
According to Lemma A.3.2 w ∈ Pre f ([tk w ]s ). Since w leads to an error state from s in
AG , the trace tk w will also lead to an error state from s in AG .
Lemma A.3.4. Let s be a state in AR . Let w = t1 ..tn be a trace in AG leading to an error state
a a am
from s. Let wa = a1 ..am be a trace in AR . Let s = s1 →1 s2 →2 ...sm → sm+1 = sa be the states it
goes through in AR . Let ti ∈
/ r(s j ) and r(s j ) be a persistent set in s j for i ∈ {1..n} and j ∈ {1..m}.
Then w will lead to an error state from sa in AG and w ∈ Pre f ([wa · w]s ).
A.3. POR Correctness Proof: Assertion Violations Preserving 135
IB : |wa | = 0
In this case sa = s. Thus w leads to an error state from sa in AG by assumption. And
wa · w = w thus clearly w ∈ Pre f ([w]s ) = Pre f ([wa · w]s ).
Lemma A.3.5. Let s be a state in AR . The reduction function r satisfies the conditions C1 and
C2 as defined in Section 3.1.3. Let w = t1 ..tn be a non-empty trace in AG that leads to an error
state from s.
t1
Then there exists a (possibly empty) trace wa = a1 ..am that goes through the states s = s1 →
t2 ta
s2 → ...sa → sa+1 = sa in AR such that w ∈ Pre f ([wa · w]s ) and w leads to an error state from sa
in AG and at least one transition ti of w is in r(sa ).
Proof. Since by assumption the execution of w from s is defined in AG , the first transition of w
is enabled in s, so t1 ∈ en(s). Thus according to condition C2 of the reduction function r, there
exists a trace in AR such that a state s is reached from s with t1 ∈ r(s ). A weaker condition is that
there exists a trace in AR from s such that a state s is reached where any ti ∈ r(s ) for i ∈ {1..n}.
t1 t2 ta
Let wa = a1 ..am be such a (possibly empty) trace in AR and s = s1 → s2 → ...sa → sa+1 = sa be
the states it goes through in AR . W.l.o.g. ¬∃ti ∈ r(s j ) for j ∈ {1..m} (else a prefix of wa can be
used). Condition C1 ensures that all r(s j ) are persistent sets for j ∈ {1..m}. According to the
above Lemma A.3.4, w will also lead to an error state from sa and w ∈ Pre f ([wa · w]s ), which is
the other result part of the proof.
Theorem A.3.6
Let s be a state in AR . The reduction function r satisfies the conditions C1 and C2 as defined
in Section 3.1.3. Let w be a trace in AG that leads to an error state. Then there exists a trace
wr in AR such that w ∈ Pre f ([wr ]s ).
IB : |w| = 0.
The base case immediately holds with wr = w.
According to Lemma A.3.2 there exists a trace w = t1 ..tk−1tk+1 ..tn in AG such that w ∈
Pre f ([tk · w ]sa ). Since w ∈ Pre f ([wa · w]s ) has already been established above, it follows
that w ∈ Pre f ([wa · tk · w ]s ).
According to Lemma A.3.3 tk · w leads to an error state from sa . Let s be the successor of
sa when executing tk . Since sa is in AR and tk ∈ r(sa ), s is also a state in AR . By definition
of w = t1 ..tk−1tk+1 ..tn , |w | = |w| − 1. Thus by IH. there exists a trace wr in AR such that
w ∈ Pre f ([wr ]s ).
Together with the already established intermediate result w ∈ Pre f ([wa ·tk ·w ]s ) it follows
that w ∈ Pre f ([wa · tk · wr ]s ), where wa · tk · wr is a path from s in AR .
From the above theorem it immediately follows that Theorem A.3.1 is also valid, since the
above theorem holds for every state in AR , hence also for the initial state. Consequently AR is a
trace automaton of AG and thus preserves all assertion violations.
First some auxiliary results will be established. The following lemma states that once a state
s is covered by another state s , executing a single transition t, which is enabled in s (and hence
has to be enabled in s due to s s ), preserves the covered relation in the resulting states.
t
Lemma A.4.2 (Result Coverage Preserving). If s s then ∀t ∈ T with t ∈ en(s) it holds s →
−
t
s →
−.
w w
Proof. By assumption s s , thus for all w ∈ T ∗ :⊥ (s − →) =⇒ ⊥ (s − →). Since w is an
arbitrary trace, let w start with the transition t followed by an arbitrary trace w : w = t · w . So
t·w t·w t w t w
⊥ (s −−→) =⇒ ⊥ (s −−→), which is equivalent to ⊥ ((s →
− ) −→) =⇒ ⊥ ((s →
− ) −→). Thus
t t
s→
−s → −.
Theorem A.4.3
Let AR be a reduced state space using the reduction function from Definition 13. Let s be a
state in AR and w a trace in AG leading to an error state se in AG . Then an error state se will
be weakly reachable from s in AR by trace w, such that se se holds.
IB |w| = 0
This case immediately holds, since the empty trace does not change the current state and
the relation is reflexive.
IS |w| = x + 1 = n.
Let w = t1 ..tn = t1 · w , where t1 is the first transition and w the rest of w. Now according
to Definition 13 of the reduction function for SSR there are two possible cases:
case r(s) = 0/
Since, by assumption, w is executable from s in AG , en(s) = 0. / Thus according to
Definition 13 there exists a state s in AR with s s and r(s ) = en(s ). Since s s
and w leads to an error state se from s in AG , w will also lead to an error state se from
s in AG . Thus t1 ∈ r(s ) = en(s ). Let st be the successor of s in AR when executing
transition t1 . The state st is weakly reachable from s, since s s . The error state se
is reachable from st by w in AG . By IH. an error state se is weakly reachable from
st in AR by trace w . According to Lemma A.4.2 se se and se se holds, which
immediately implies se se .
case r(s) = en(s)
Since w is executable in AG from s, t1 ∈ en(s) = r(s). Let st be the successor of s in
AR when executing transition t1 . The state st is weakly reachable from s, since it is
directly reachable from s. The error state se is reachable from st by w in AG . By IH.
an error state se is weakly reachable from st in AR by trace w . According to Lemma
A.4.2 se se holds.
The main theorem (Theorem A.4.1) of this section follows immediately from the above the-
orem, since it is valid for any state s in AR , thus also for the initial state s0 .
The idea is to proof this theorem by induction over the length of the trace w. But first some
auxiliary results are established.
Lemma A.5.2. Let s be a state in AR . Let w = t1 ..tn be a trace in AG leading to an error state
from s. Let wa = a1 ..am be a trace in AR , such that a state sa in AR is weakly reachable from
s by wa . Let s = s1 ..sk+1 = sa be the weakly reachable states that it goes through in AR . Let
ti ∈
/ r(s j ) and r(s j ) is a persistent set in s j , for all i ∈ {1..n} and for all j ∈ {1..k}. Then w will
lead to an error state from sa in AG .
138 A. Appendix
Proof. The proof proceeds by induction over the length the trace w.
IB : |w| = 0.
In this case s sa by Definition 16 of weak reachability. Thus w leads to an error state
from sa in AG by Definition 14 of .
IS : |w| = x + 1 = m.
a
In this case s1 s2 and s2 −→ 1
R s3 and sa is weakly reachable from s3 by the rest a2 ..am
of wa by Definition 16 of weak reachability. Analogously to IB. w will lead to an error
t1 t2 tn
state from sa in AG . Let s2 = z1 → z2 → ...zn → zn+1 be the states w goes through in AG .
None ti of w is in the persistent set T = r(s2 ) of s2 by assumption. Thus by Definition 10
of persistent sets, a1 is independent with t j in z j for j ∈ {1..n}. According to Definition
6 of trace equivalence a1 · w ∈ [w · a1 ]s2 , which means executing a1 before or after w from
s2 will lead to the same state. Consequently, w will lead to an error state from s3 in AG .
Thus by IH. w will lead to an error state from sa in AG .
Lemma A.5.3. Let s be a state in AR , which satisfies the conditions C1 and C2W as defined in
Section 5.3. Let w = t1 ..tn be a non-empty trace in AG leading to an error state from s. Then
there exists a (possibly empty) trace wa in AR such that a state sa is weakly reachable from s in
AR , where at least one transition ti of w is in r(sa ) and w leads to an error state from sa in AG .
These auxiliary lemmata together with some of those already introduced in Section A.3 can
be used to proof the following main theorem.
Theorem A.5.4
Let s be a state in AR . The reduction function r satisfies the conditions C1 and C2W as
defined in Section 5.3. Let w be a trace in AG leading to an error state from s. Then there
exists a trace wr in AR such that an error state is weakly reachable from s in AR .
IB : |w| = 0
This case immediately holds with wr = w.
A.6. Reduction Function Condition Proofs 139
From the above theorem it immediately follows that Theorem A.5.1 is also valid, since the
above theorem holds for every state in AR , hence also for the initial state.
Theorem A.6.2
The condition C2 is implied by the condition C1 and C2S .
Lemma A.6.3. Let s be a state in AR , where the reduction function satisfies the conditions
C1 and C2WS . Let w = a ..a be a trace in A , such that a fully expanded state s is weakly
a 1 m R a
reachable from s in AR . Let s = s1 ..sk = sa be the weakly reachable states wa goes through in
AR . Let w = t1 ..tn be a non-empty trace leading to an error state from s in AG . Then t ∈ r(s j )
for some j ∈ {1..k}.
IB |w| = 0
In this case s sa by Definition 16 of weak reachability. Since w leads to an error state
from s in AG , w will also lead to an error state from sa in AG , by Definition 14 of .
Thus the first transitions t1 of w is enabled in sa . By assumption sa is fully expanded, i.e.
r(sa ) = en(sa ), thus t1 ∈ r(sa ).
IS |w| = x + 1 = m
a
In this case s1 s2 and s2 −→
1
R s3 and sa is weakly reachable from s3 by the rest a2 ..am of
wa by Definition 16 of weak reachability.
t t
Analogously to the IB. case, w will lead an error state from s2 in AG . Let s2 = z1 →
1
z2 →
2
t1
...z1 → z1+1 n be the states w goes through in AG . Now there are two possible cases:
– ∃i ∈ {1..n} : ti ∈ r(s2 ) : in this case the proof is complete.
– ∀i ∈ {1..n} : ti ∈
/ r(s2 ) : the condition C1 ensures that r(s2 ) is a persistent set in s2 .
None ti of w is in the persistent set r(s2 ) of s2 in this case. Thus by Definition 10 of
persistent sets, a1 is independent with t j in z j for j ∈ {1..n}. According to Definition
6 of trace equivalence a1 · w ∈ [w · a1 ]s2 , which means executing a1 before or after w
from s2 will lead to the same state. Consequently, w will lead to an error state from
s3 in AG . Thus by IH. t ∈ r(s j ) for some j ∈ {3..k}.
Theorem A.6.4
S .
The condition C2W is implied by the conditions C1 and C2W
S .
Proof. Let s be a state in AR , where the reduction function satisfies the conditions C1 and C2W
Let w be a non-empty trace leading to an error state from s in AG . Let t be the first transition
of w. Since w is executable from s in AG , t is enabled in s, i.e. t ∈ en(s). According to C2W
S ,
there exists a trace wa in AR , such that a fully expanded state sa is weakly reachable from s in
AR . Then according to Lemma A.6.3 there exists a weakly reachable state s from s in AR such
that t ∈ r(s ).
main loop The algorithm begins by initializing the initial state and then pushes it on the
search stack. Now it will loop until the search stack is not empty. In every iteration the top
state s of the search stack is removed. First it will be checked whether s has already been
expanded. This is the case if s was already on the stack and has been re-pushed. In this case
it will be checked whether the working set of s is empty, which means the exploration of s is
complete. If it is, s will be backtracked, else a transition from s will be executed by calling
the runStep function. Else s was not yet expanded in Line 7, thus it will first be expanded and
then a transition from s will be executed. In the following these three main functions, namely
expand, backtrack and runStep will be further explained one after another. Each of them gets
the currently examined state s as argument.
expand The expand(s) function first checks whether s has already been visited. If so, the
already visited state v is retrieved. If v is safe, then all states that can reach it are also marked
safe. Else v is marked as unfinished, thus it will eventually be refined during backtracking. An
exception is used to stop the execution of this path, since s has already been visited, and transfer
the control back to the main loop, which will then examine the next state on the stack. Only
states where the kernel is in the evaluation or notification phase of the simulation are stored
in the cache. This is just a design decision, since these phases are the important one for the
simulation, as described in Section 2.1.1. Else s has not yet been visited. First s is added to the
set of visited states. Then if s is in the evaluation phase of the simulation, a persistent set will
be computed in s, starting from some enabled transition, and assigned to s.working. If s is fully
expanded or in the notification phase of the simulation, in which case it is fully expanded by
definition, s and all states that can reach it will be marked safe.
backtrack The backtrack(s) function will check whether some relevant transition may have
been ignored from the state s. This is the case if s lies on a cycle of states (s.unfinished is true)
and does not reach a fully expanded state (s.safe is not true). In this case s will be refined. The
implementation shown in Algorithm 22 will simply fully expand the state s and than mark s and
all states on the search stack, since they can reach s, as safe. States are marked in reverse order
as an optimization. This allows to stop the marking once a safe state is detected, since all states
below on the stack must already be safe.
142 A. Appendix
runStep The runStep(s) function starts or continues the execution of a single transition. The
actual behaviour depends on the current simulation phase and whether the execution of a tran-
sition has been interrupted. A transition will be interrupted, whenever a branch with a symbolic
condition c is executed where both c and ¬c are satisfiable with the current path condition. The
execution of the current transition will fork into two independent execution paths sT and sF .
The path condition of sT one state with c and the other one with ¬c as described in Section
2.3. These states are said to be forked. The predicate s.simState.isFork will return True for such
states. Given a state s, the forked state can be accessed via s. f orked.
Now the behaviour of the runStep function can be described as follows: If the simulation is
completed, the function will simply return. Else if the simulation in state s is in the evaluation
phase, s will be cloned into the state n. Cloning s ensures that the stored copy in the visited
set is not modified and other transitions can be explored from s later on. The original state s is
re-pushed on the stack, thus it will be examined from within the main loop again. At that point s
will be either backtracked or a different transition will be explored from s. Next, an unexplored
transition from the persistent set in s is selected and explored from the state n. Selection happens
from s, to modify the search informations associated with s and execution happens from n to
keep the actual state s unmodified. Similarly the state s will be cloned if the simulation is in
the notification phase. The notification phase, which comprises the update, delta notify and
optional timed notification phases, is executed from the cloned state n. All other simulation
phases, or an interrupted transition, have only a single choice, thus they will simply continue
execution. Since, by a design decision, these states are not stored in the visited set, they are
not cloned, but directly transfered to a new state. In all cases the successor state n is reached,
which is pushed on the search stack. If the transition execution had been interrupted due to a
symbolic branch, the forked state will also be pushed on the stack for further examination. Thus
the search algorithm just puts both of them onto the search stack for further exploration. Doing
so seems to be a natural choice since both forked states, which represent execution paths, are
completely independent of one another. Since all of its predecessor states are below them on
the stack, such an implementation is compatible with the cycle proviso C2 .
23).
Dependent transitions are only added to the working set of s during backtracking, if they are
in the persistent set initially associated with s (Line 67)4 . If the analysis fails to infer a non
trivial persistent set in a state (Line 69), or when a cycle of states is detected and expanded
Line 35, the states are not fully expanded, but restricted to the initially computed persistent set.
Only in Line 31 all enabled transitions will be explored that have not yet been explored. This is
required to solve the ignoring problem as has already been discussed in Section 3.2.2. Thus it
cannot be restricted to the initially computed persistent set.
Lemma A.9.1. For every cycle of states that is explored by the DPE+CT algorithm in the same
delta cycle, there exists at least a state s in the cycle of states such that s is fully expanded.
Proof. Transitions are not interrupted preemptively. They are always executed until the next
context switch or the end of their thread. Consequently whenever a transition of thread p is
executed from state s reaching state s , the next transition of p is disabled (or not available if the
end has been reached) in s .
The only way to re-enable a thread p in the same delta cycle is to use an immediate notifica-
tion (according to the semantics of the IVL). This requires that (a) p waits for an event and (b)
another thread immediately notifies that event after p has been executed (else the notification
would be lost).
These two requirements need to be satisfied in order to form a cycle of states in the same delta
cycle. Let s be a state in delta cycle d. Let w = t1 ..tn be a trace that goes through the cycle of
t1 t2 tn
states s = s1 → s2 → ...sn → sn+1 = s. In order two form a cycle of states, at least one transition
has to be executed, thus w is not empty. The cycle requirements (a) and (b) immediately imply
that:
• At least two threads are required to form a cycle of states in the same delta cycle.
4 Actually if their thread corresponds to a thread of any transition in the persistent set, as defined in [FG05].
146 A. Appendix
notify e1
ti
s wait e1
ta
t1
• Every transition in the cycle executes a wait event statement as context switch.
• There exists a transition ta , which might be equal to t1 , in w such that next(sa+1 , thread(ta ))
= t1 and thread(ta ) = thread(t1 ).
• There exists a transition ti in w after ta , such that thread(ti ) = thread(ta ) and ti immedi-
ately notifies the event that t1 waits for.
So t1 and ta are transitions that belong to the same thread (they can also be equal). Without
loss of generality ta is the last transition of the thread(ta ) = thread(t1 ) that is executed before
t1 . Thus all transitions ta+1 ..tn are from a different thread. Since ti is executed after ta , the
thread(ta ) is not enabled in si . Since ta waits for an event that ti immediately notifies, ta is
dependent with ti in si . The principle is shown in Figure A.1.
Consequently every valid (statically precomputed) dependency relation will capture this de-
pendency. The conflicting transitions (CT) algorithm will return the complete set of enabled
transitions whenever a disabled transition is introduced in the persistent set. Since ti is in the
persistent set of si and ta is disabled in si , the CT algorithm must have returned all transitions
enabled in si . Thus si , which is a state of the cycle, is fully expanded.
Lemma A.9.2. For every cycle of states explored by the DPE+CT algorithm there exists a state
s in the cycle of states, such that s is fully expanded.
Proof. According to Lemma A.9.1 for every cycle of states in the same delta cycle, at least one
state is fully expanded. A cycle of states that spans multiple delta cycles, must by definition of
delta cycles contain a fully expanded state. The reason is that between every two delta cycles
there exist at state s, such that only the designated transition tN is enabled in s. Basically the
transition tN denotes the execution of a notification phase from s. The successor state will
then be in the eval phase of the next delta cycle. So in both cases, the cycle of states spans
multiple delta cycles or is contained in a single delta cycle, at least one state of the cycle is fully
expanded.
Theorem A.9.3
The deadlock preserving exploration (DPE) algorithm, presented in Section 3.2.1, where
the conflicting transitions (CT) algorithm is used to compute static persistent sets, denoted
as DPE+CT, will also preserve all assertion violations for any IVL program.
Proof. It has already been show in Section 3.2.1 that the DPE algorithm satisfies condition C0
and C1 . If the state space does not contain any cycles, the DPE algorithm will for every state s
eventually reach a terminal state, since the state space is finite. By definition a terminal state is
fully expanded. According to Lemma A.9.2 in every cycle of states there exists a state, which
is fully expanded. Consequently every state that can reach any state of the cycle can reach that
A.9. Solving the Ignoring Problem implicitly 147
fully expanded state. It follows immediately that from every state s the DPE algorithm will
reach a state s which is fully expanded. So condition C2S is satisfied too, which implies the
weaker condition C2 . Thus all assertion violations are preserved.
Lemma A.9.4. For every cycle of states in the same delta cycle explored by the A-SDPOR
algorithm, there exists a state s in the cycle of states, such that s is fully expanded, that means
s.done = enabled(s) when s is finally backtracked.
Proof. In the proof of Lemma A.9.2 it has been shown that for every cycle of states that appears
in the same delta cycle there always exists two transitions ta and ti , such that ta is dependent
with ti in si and and thread(ta ) is not enabled in si (so neither transition of thread(ta ) is enabled
in si ). Whenever a cycle of states is detected, the backtrack analysis of the A-SDPOR algorithm
will, by definition of the A-SDPOR algorithm, compare all transitions of the cycle with each
other. Thus ta is compared with ti in state si . So the dependency is detected. Since thread(ta ) is
not enabled in si , which means that neither transition of thread(ta ) is enabled, the next transition
of next(si ,thread(ta )) is also not enabled. Hence the algorithm will add all enabled transitions
to the working set of si which have not already been explored. Thus all enabled transitions will
be explored from si .
Lemma A.9.5. For every cycle of states explored by the A-SDPOR algorithm there exists a
state s in the cycle of states, such that s is fully expanded, that means s.done = en(()s) when s
is finally backtracked.
Proof. According to Lemma A.9.4 for every cycle of states in the same delta cycle, at least one
state is fully expanded. A cycle of states that spans multiple delta cycles, must by definition of
delta cycles contain a fully expanded state, as described in Lemma A.9.2.
Thus the A-SDPOR algorithm would fully expand a state in each cycle of states, therefore
satisfying the cycle proviso C2S . Since the C-SDPOR algorithm is built on top of the A-SDPOR
algorithm, the same result applies to it. Thus whenever a more sophisticated algorithm is em-
ployed in the C-SDPOR algorithm to handle cycles of states, the cycle proviso will still be
preserved.
5 The proviso could be violated when a more sophisticated backtracking dependency analysis is employed, but
that part of the algorithm is not directly dependent with the way cycles of states are handled to ensure that
persistent sets are explored in each state.
148 A. Appendix
A.9.3. Discussion
First of all these algorithms discussed in the previous sections do not solve the ignoring problem
in general, but only when certain conditions are met. One of them is, that a simulation seman-
tics is used that is similar to those of IVL (which corresponds to the semantics of SystemC)
programs. The other condition is, that the exploration algorithms compute persistent sets in a
compatible way. It turns out that the static CT algorithm and the backtracking analysis of the
A-SDPOR algorithm, when it does not fall back to persistent sets, are compatible.
So it might be unnecessary to use an extended algorithm that explicitly solves the ignoring
problem. This offers some slight advantages. First of all a less complex algorithm has to be
implemented or if a stateless algorithm is already available it can be immediately be extended
to a stateful version, simply by tracking already visited states. And second the additional per-
formance and memory overhead6 is removed, since it is not necessary to keep track of safe and
unfinished states and traverse the search stack to mark safe states.
Though it seems that using the DPE+CT algorithm will not yield any further reduction com-
pared to the AVPE+CT algorithm. The reason is that the AVPE algorithm is based on the DPE
algorithm. So using AVPE+CT there also will be a state s for every cycle of states, such that all
enabled transitions in s are explored. Thus the specific extensions to solve the ignoring problem
explicitly will not introduce any further refinements, since every state of the cycle has already
to be marked safe.
So altogether the unextended algorithm that solves the ignoring problem implicitly offers
only negligible lower resource usage and does not offer a better reduction than a more so-
phisticated algorithm that requires an explicit extension to solve the ignoring problem. The
AVPE+STUB combination normally performs a better state space reduction than the DPE+CT
combination. Though the implementation of DPE+CT is less complex and only requires a valid
dependency relation between transitions, a can-enable relation is not required.
Definition 32
A state s1 is covered by a state s2 , denoted as s1 ! s2 , iff ((C1 and C2 ) or C3 ). With:
C1 := s1 ∼ s2
C2 := ESS (s1 , s2 )
C3 := s1 is a terminal state (e.g. pc(s1 ) is unsatisfiable or no more transition is enabled)
In the following the relation ! is referred to, when it is said that a state covers another one.
The relation will always be explicitly specified. Since the ESS algorithm is used for matching,
the above condition C2 holds by definition. Furthermore condition C1 is also satisfied, since the
ESS algorithm (symbolic state comparison in general) will only be applied if two states are
structurally compatible. Condition C3 is useful for the following proof, because if no transition
is executed from a state s, then s s for all states s by definition of .
Now it is left to show that s1 ! s2 implies s1 s2 . The proof is outlined in the following.
The idea is to show that for every trace w ∈ T ∗, either its execution from s2 will lead to an error
state, or its execution from s1 will not lead to an error state and the property s1 ! s2 is preserved
w w
in the successor states s1 −→! s2 − →. This property will be called (trace) error coverage. First
it will be shown that executing a single statement preserves the above properties (single step
error coverage). This will then be extended to multiple statements (multi step error coverage).
Its direct implication is that whenever the execution of a statement reaches an error state in s1 it
will also reach an error state in s2 .
These proofs rely on three helper lemmas. The first one states that extending the path condi-
tion with an expression constructible in both states preserves the covered relation. The second
one that assigning a value to a variable preserves the covered relation. And the third, that any
condition that can be constructed in either state and is satisfiable in s1 is also satisfiable in s2 .
All of them follow quite naturally from the definition of the ESS method, since every value that
can be constructed in s1 can also be constructed in s2 .
The next section defines and proves the helper theorems. The section thereafter defines and
proves single step-, multi step- and trace error coverage. This directly implies the desired prop-
erty s1 ! s2 =⇒ s1 s2 .
A sufficient condition to be able to construct linked expressions is that both states are struc-
turally compatible s1 ∼ s2 or a stronger condition is that one state is covered by the other s1 ! s2 .
Definition 34 (Value Covered)
Let s1 ! s2 and μ be an arbitrary linked expression in s1 and s2 . If s1 [μ] can evaluate to a
concrete value v (denoted as v ∈ s1 [μ]) and s1 ! s2 then s2 [μ] can also evaluate to v. It will
be denoted as s1 [μ] ⊆ s2 [μ].
150 A. Appendix
Its like adding a new global variable slot (with the same unique name) to s1 and s2 and
assigning s1 [μ], s2 [μ] to it resulting in the states s̃1 and s̃2 . Then s1 ! (μ)s2 iff s̃1 ! s̃2 .
Definition 36
A condition c is satisfiable in a state s denoted as sat(s, c) iff pc(s) ∧ c is satisfiable.
Lemma A.10.1 (Expression Preserving). Let μ be an arbitrary expression that can be con-
structed in state s1 , denoted as s1 [μ]. If s1 ! s2 then s1 ! (μ)s2 .
Proof. Let μ be a value that is constructed using the same operations in both states s1 and
s2 , denoted as s1 [μ] and s2 [μ] respectively. This is possible because s1 and s2 are structurally
compatible (s1 ∼ s2 ), by def. of s1 ! s2 .
By structural induction over μ it will be shown that s1 ! (μ)s2 is valid. The base cases for the
induction are the literal expression constructors. The operators will be handled in the induction
step. The expressions considered in the proof are shown in Figure A.2. They represent the
relevant expressions in the SystemC IVL.
IB: case μ = x, where x is a concrete value (x = int|bool|char).
This case is satisfied because s1 [x] = s2 [x] is always true without further assump-
tions.
case μ = id
In this case, the identifier will be resolved and the corresponding variable value v
will be returned. Since the same identifier is used in both states at the same program
location, the same variable slot (which contains the variable value) will be selected.
By assumption s1 ! s2 . Thus vars(s1 )[id] ⊆ vars(s2 )[id] for any identifier id.
case μ = ?(type)
A fresh (fully unconstrained) symbolic literal with the same type can evaluate to the
same values in both states. Since μ is fresh it will not interfere with any existing
value.
case μ = new <type>
A newly allocated pointer will have the same default value on both states. Which
is either the concrete value undefined, the numeric literal zero, or a fresh symbolic
literal. Either case has already been handled.
IS: case μ = a + b
By IH. s1 ! (a)s2 and s1 ! (b)s2 . Thus it immediately follows that s1 ! (μ =
a + b)s2 .
This proof case is applicable to any n-ary expression, not just binary addition. Thus
all other binary- and unary- expressions as well as the array index access expression
are satisfied too.
A.10. Correctness Proof of the Exact Symbolic Subsumption 151
Figure A.2.: Excerpt of relevant literal and compound expression constructors in the IVL
Basically Lemma A.10.1 states that every value v that can be constructed in s1 can also
be constructed in s2 , while preserving ESS (s1 , s2 ) for all v. It allows to directly show the
following theorem.
Lemma A.10.2 (Satisfiability (SAT) Covered). If s1 ! s2 then sat ! (s1 , s2 ).
Proof. Let c be an arbitrary linked condition of the states s1 and s2 . According to Lemma
A.10.1 if c can evaluate to b in s1 then it can evaluate to b in s2 . So if c can evaluate to true in
s1 (is satisfiable in s1 ), then it can also evaluate to true in s2 (is satisfiable in s2 ).
Lemma A.10.3 (Path Condition Preserving). If s1 ! s2 and the path conditions of both states
are extended with an arbitrary linked condition c, resulting in the states s̃1 and s̃2 , defined as
s̃1 = s1 except pc(s̃1 ) := pc(s1 ) ∧ s1 [c]
s̃2 = s2 except pc(s̃2 ) := pc(s2 ) ∧ s2 [c]
then s̃1 ! s̃2 (the covered relation will be preserved).
Proof. This immediately follows from Lemma A.10.1, since s1 ! s2 by assumption.
Lemma A.10.4 (Assignment Preserving). If s1 ! s2 and the assignment v = μ is executed in
both states, where μ is a linked expression, resulting in the states s̃1 and s̃2 , defined as
s̃1 = s1 except vars(s̃1 ) := vars(s1 )[v ← s1 [μ]]
s̃2 = s2 except vars(s̃2 ) := vars(s2 )[v ← s2 [μ]]
then s̃1 ! s̃2 .
152 A. Appendix
Proof. Since s1 ! s2 also s1 ∼ s2 (by def. of !). Thus v refers to the same variable slot in
both states. So both states will modify the same variable and leave everything else unmodi-
fied. Consequently structural compatibility is preserved (s̃1 ∼ s̃2 ), because either s1 [μ] is either
completely equal to s2 [μ] or at least one of them is symbolic. According to Lemma A.10.1
s1 ! (μ)s2 , which then immediately implies s̃1 ! s̃2 .
Basically the above definition states that either the successor of s2 is an error state, or the
successor of s1 is not an error state and is covered by the successor of s2 . Thus if ⊥ (s̃1 ) then
⊥ (s̃2 ) too. Single step coverage can be extended to multi step coverage. If an error state is
reached from s1 executing a path p, then there exists a prefix of p such that an error state will
be reached executing that prefix from s2 . A path is a sequence of statements l1 ..ln . A prefix of
a path is a sequence of statements l1 ..lk where k ≤ n. If k < n then it is a real prefix. The length
of a path p will be denoted as |p|.
Definition 39 (Multi Step Error Covered)
A state s1 is multi step error covered by s2 , denoted s1 !∗⊥ s2 , iff
∀l1 ..ln ∈ Statement ∗ : (∀i ∈ {1..n} :⊥ (s˜2 i ) ∨ [∀ j ≤ i : ¬ ⊥ (s˜1 j ) ∧ (s˜1 j ! s˜2 j )])
l ..l l ..l
where s˜1 i = s1 −1−→
i
and s˜2 i = s2 −1−→
i
for all i.
Remark. According to Definition 39 whenever the execution of a path p = l1 ..ln from s1 leads
p
to an error state ⊥ (s1 −
→), then the execution of a prefix of p from s2 will also lead to an error
p q p p
state ⊥ (s2 −
→). If ¬ ⊥ (s2 →− ) where q is any real prefix of p, then ⊥ (s1 −→) !⊥ (s2 − →) will
hold. The reason that s2 might already hit an error state when executing a real prefix of p is that
s2 can (normally) assume more values than s1 . Thus an assertion violation might occur on a real
prefix of p. If all assertions are rewritten as conditional gotos, then both paths of them would
be feasible in that case. Thus s2 could always reach a corresponding error state to s1 .
Theorem A.10.5
If s1 is covered by s2 , then s1 is also single step covered by s2 , i.e. s1 ! s2 =⇒ s1 !⊥ s2
A.10. Correctness Proof of the Exact Symbolic Subsumption 153
Proof. The proof will consider all possible cases. Each case corresponds to a different state-
ment. A list of valid IVL statements is available in Figure A.3. The assumption is that s1 ! s2
holds before the execution. After execution of a single statement t, the states s̃1 and s̃2 are
reached. It will be shown that s̃1 !⊥ s̃2 then will be valid.
The observation is, that an error state is only reached if an assertion violation is detected7 .
Thus only the assert statement can have influence, whether ⊥ (s̃1 ) or ⊥ (s̃2 ). So for all other
statements ¬ ⊥ (s̃1 ) and ¬ ⊥ (2̃1 ). Thus it has to be shown for them, that they preserve the !
relation.
Execution of any statement will modify the program counter. Either it will be incremented or
in case of a goto set to the target label position. Since both states start with the same program
counter, they also end up with the same program counter. So changes to the program counter
(which are orthogonal to other state changes) will not invalidate the ! relation.
Statements that do not use or modify symbolic state parts also trivially preserve the ! rela-
tion, as they start with the same concrete state parts and apply the same operations, thus they end
up again with the same concrete state parts. Among other all statements that interact with the
simulation kernel belong into this category: wait event, wait time, notify event,
resume, suspend. All other statements will be considered in the following.
case assignment (v = e)
Is satisfied according to Lemma A.10.4.
– ⊥ (s̃1 ) : In this case ⊥ (s̃2 ) too, since according to Lemma A.10.2 sat(s1 , s1 [¬c]) =⇒
sat(s2 , s2 [¬c]).
– ¬ ⊥ (s̃1 ) : In this case it is unknown whether ⊥ (s̃2 ). If ⊥ (s̃2 ), then s̃1 !⊥ s̃2
(regardless whether s1 will eventually reach an error state, an error state is reached
from s2 on a real prefix of that path). If ¬ ⊥ (s̃2 ) then s˜1 ! s˜2 is trivially valid, since
both s1 and s2 are not changed8 .
s2 then s1 ! s2 due to Lemma A.10.3 (extending the path condition preserves the covered
relation).
If a branch is infeasible in s1 then the corresponding execution path will not be considered.
Since it is not executed at all, it cannot reach an error state. Thus by definition it is covered
by the corresponding path in s2 regardless whether it will be continued or not.
Remark. Every time a single statement is executed in s, the successor of s, denoted s̃, can be
one of the following: an error state, a terminal state, one normal state or two normal states (due
to symbolic branch condition).
Assuming s1 ! s2 and both states execute the same statement resulting in s̃1 and s̃2 respec-
tively, then according to the single step error coverage Theorem A.10.5 the following properties
are valid:
• If s̃1 is a terminal state, then s̃1 ! s̃2 trivially holds, since s̃1 cannot reach any error state.
• If s1 reaches one successor state s̃1 , then s2 will also reach one successor state s̃2 such that
s̃1 ! s̃2 .
• If s1 reaches two successor state s̃1T and s̃1F (due to a symbolic branch condition), then
s2 will also reach two corresponding successor states s̃2T and s̃2F such that s̃1T ! s̃2T and
s̃1F ! s̃2F .
The following theorem states, that single step error coverage can be extended to multi step error
coverage.
Theorem A.10.6
Single step coverage implies multi step coverage, i.e. s1 !⊥ s2 =⇒ s1 !∗⊥ s2 .
Proof. Multi step error coverage can be shown by induction over the length of the path p,
l ..l
assuming single step error coverage. Similarly to Definition 39 the abbreviations s˜1 i = s1 −1−→
i
l1 ..li
and s˜2 i = s2 −−→ will be used for all i.
IB: |p| = 0
Since no statement is executed at all, s1 !∗⊥ s2 holds by definition.
A.10. Correctness Proof of the Exact Symbolic Subsumption 155
IS: |p| = n + 1 and by IH. s1 !⊥ s2 =⇒ s1 !∗⊥ s2 for all paths q with |q| ≤ n.
By IH. ∀t = l1 ..ln ∈ Statement ∗ : (∀i ∈ {1..n} :⊥ (s˜2 i ) ∨ [∀ j ≤ i : ¬ ⊥ (s˜1 j ) ∧ (s˜1 j ! s˜2 j )]),
thus ⊥ (s˜2 n ) or (∀ j ≤ n : ¬ ⊥ (s˜1 j ) ∧ (s˜1 j ! s˜2 j )), since these conditions are valid for all i
in {1..n}. So either (1) ⊥ (s˜2 n ) or (2) (∀ j ≤ n : ¬ ⊥ (s˜1 j ) ∧ (s˜1 j ! s˜2 j )). Case (2) directly
implies ¬ ⊥ (s˜1 n ) and s˜1 n ! s˜2 n , since it must be valid for any j in {1..n}.
(1) : Since s˜1 n ! s˜2 n by IH., according to Definition 38 s˜1 n !⊥ s˜2 n , together they imply
s1 !∗⊥ s2 .
(2) : ⊥ (s˜2 n ) implies ⊥ (s˜2 n+1 ) by definition (once an error state has been reached, it
will stay an error state). Together with the IH., this implies s1 !∗⊥ s2 .
Proof. Transition and trace coverages are just special cases of multi step coverage, since every
trace is a sequence of transitions, which are sequences of statements. Thus assuming s1 ! s2
w w
then according to Theorem A.10.6 for all traces w = l1 ..ln : ⊥ (s2 −
→) ∨ ¬ ⊥ (s1 −→), which is
w w
equivalent to ⊥ (s1 −
→) =⇒ ⊥ (s2 − →).
The main Theorem A.10.7 directly implies the desired property s1 ! s2 =⇒ s1 s2 .
Bibliography
[APV06] Saswat Anand, Corina S. Păsăreanu, and Willem Visser. “Symbolic Execution
with Abstract Subsumption Checking”. In: Proceedings of the 13th International
Conference on Model Checking Software. SPIN’06. Vienna, Austria: Springer-
Verlag, 2006, pp. 163–181. ISBN: 3-540-33102-6, 978-3-540-33102-5. DOI: 10.
1007/11691617_10. URL: http://dx.doi.org/10.1007/11691617_10.
[BH05] Dragan Bošnački and GerardJ. Holzmann. “Improving Spin’s Partial-Order Re-
duction for Breadth-First Search”. English. In: Model Checking Software. Ed.
by Patrice Godefroid. Vol. 3639. Lecture Notes in Computer Science. Springer
Berlin Heidelberg, 2005, pp. 91–105. ISBN: 978-3-540-28195-5. DOI: 10.1007/
11537328_10. URL: http://dx.doi.org/10.1007/11537328_10.
[BK10] Nicolas Blanc and Daniel Kroening. “Race Analysis for Systemc Using Model
Checking”. In: ACM Trans. Des. Autom. Electron. Syst. 15.3 (June 2010), 21:1–
21:32. ISSN: 1084-4309. DOI: 10.1145/1754405.1754406. URL: http://doi.
acm.org/10.1145/1754405.1754406.
[Bla+09] D. Black et al. SystemC: From the Ground Up. 2nd. Secaucus, NJ, USA: Springer-
Verlag New York, Inc., 2009.
[BLL06] Dragan Bošnački, Stefan Leue, and AlbertoLluch Lafuente. “Partial-Order Re-
duction for General State Exploring Algorithms”. English. In: Model Checking
Software. Ed. by Antti Valmari. Vol. 3925. Lecture Notes in Computer Science.
Springer Berlin Heidelberg, 2006, pp. 271–287. ISBN: 978-3-540-33102-5. DOI:
10.1007/11691617_16. URL: http://dx.doi.org/10.1007/11691617_16.
[BMP07] B. Bailey, G. Martin, and A. Piziali. ESL Design and Verification: A Prescription
for Electronic System Level Methodology. Morgan Kaufmann/Elsevier, 2007.
[Bok+11] Peter Bokor et al. “Supporting Domain-specific State Space Reductions Through
Local Partial-order Reduction”. In: Proceedings of the 2011 26th IEEE/ACM In-
ternational Conference on Automated Software Engineering. ASE ’11. Washing-
ton, DC, USA: IEEE Computer Society, 2011, pp. 113–122. ISBN: 978-1-4577-
1638-6. DOI: 10.1109/ASE.2011.6100044. URL: http://dx.doi.org/10.
1109/ASE.2011.6100044.
[CCH13] Chun-Nan Chou, Chen-Kai Chu, and Chung-Yang (Ric) Huang. “Conquering the
Scheduling Alternative Explosion Problem of SystemC Symbolic Simulation”. In:
Proceedings of the International Conference on Computer-Aided Design. ICCAD
’13. San Jose, California: IEEE Press, 2013, pp. 685–690. ISBN: 978-1-4799-
1069-4. URL: http://dl.acm.org/citation.cfm?id=2561828.2561961.
[CDE08] Cristian Cadar, Daniel Dunbar, and Dawson Engler. “KLEE: Unassisted and Au-
tomatic Generation of High-coverage Tests for Complex Systems Programs”. In:
Proceedings of the 8th USENIX Conference on Operating Systems Design and
Implementation. OSDI’08. San Diego, California: USENIX Association, 2008,
pp. 209–224. URL: http : / / dl . acm . org / citation . cfm ? id = 1855741 .
1855756.
[Cho+12] Chun-Nan Chou et al. “Symbolic model checking on SystemC designs”. In: De-
sign Automation Conference (DAC), 2012 49th ACM/EDAC/IEEE. June 2012,
pp. 327–333.
[Cim+10] Alessandro Cimatti et al. “Verifying SystemC: A Software Model Checking Ap-
proach”. In: Proceedings of the 2010 Conference on Formal Methods in Computer-
Aided Design. FMCAD ’10. Lugano, Switzerland: FMCAD Inc, 2010, pp. 51–60.
URL : http://dl.acm.org/citation.cfm?id=1998496.1998510.
[Cim+11] A. Cimatti et al. “KRATOS: a software model checker for SystemC”. In: Proceed-
ings of the 23rd international conference on Computer aided verification. CAV’11.
Snowbird, UT: Springer-Verlag, 2011, pp. 310–316. ISBN: 978-3-642-22109-5.
URL : http://dl.acm.org/citation.cfm?id=2032305.2032329.
[Cla+99] E.M. Clarke et al. “State space reduction using partial order techniques”. English.
In: International Journal on Software Tools for Technology Transfer 2.3 (1999),
pp. 279–287. ISSN: 1433-2779. DOI: 10 . 1007 / s100090050035. URL: http :
//dx.doi.org/10.1007/s100090050035.
[CNR11] A. Cimatti, I. Narasamdya, and M. Roveri. “Boosting lazy abstraction for systemc
with partial order reduction”. In: Proceedings of the 17th international confer-
ence on Tools and algorithms for the construction and analysis of systems: part
of the joint European conferences on theory and practice of software. TACAS’11
/ ETAPS’11. Saarbrücken, Germany: Springer-Verlag, 2011, pp. 341–356. ISBN:
978-3-642-19834-2. URL: http://dl.acm.org/citation.cfm?id=1987389.
1987430.
[CNR13] Alessandro Cimatti, Iman Narasamdya, and Marco Roveri. “Software Model Check-
ing SystemC”. In: IEEE Trans. on CAD of Integrated Circuits and Systems 32.5
(2013), pp. 774–787. DOI: 10.1109/TCAD.2012.2232351. URL: http://dx.
doi.org/10.1109/TCAD.2012.2232351.
[Eck+06] W. Ecker et al. “Specification Language for Transaction Level Assertions”. In:
High-Level Design Validation and Test Workshop, 2006. Eleventh Annual IEEE
International. Nov. 2006, pp. 77–84. DOI: 10.1109/HLDVT.2006.319967.
[EEH07] W. Ecker, V. Esen, and M. Hull. “Implementation of a Transaction Level Assertion
Framework in SystemC”. In: Design, Automation Test in Europe Conference Exhi-
bition, 2007. DATE ’07. Apr. 2007, pp. 1–6. DOI: 10.1109/DATE.2007.364406.
Bibliography 159
[EP10] Sami Evangelista and Christophe Pajault. “Solving the Ignoring Problem for Par-
tial Order Reduction”. In: Int. J. Softw. Tools Technol. Transf. 12.2 (May 2010),
pp. 155–170. ISSN: 1433-2779. DOI: 10 . 1007 / s10009 - 010 - 0137 - y. URL:
http://dx.doi.org/10.1007/s10009-010-0137-y.
[FG05] Cormac Flanagan and Patrice Godefroid. “Dynamic Partial-order Reduction for
Model Checking Software”. In: Proceedings of the 32Nd ACM SIGPLAN-SIGACT
Symposium on Principles of Programming Languages. POPL ’05. Long Beach,
California, USA: ACM, 2005, pp. 110–121. ISBN: 1-58113-830-X. DOI: 10 .
1145/1040305.1040315. URL: http://doi.acm.org/10.1145/1040305.
1040315.
[Fos09] Harry Foster. “Applied Assertion-Based Verification: An Industry Perspective”.
In: Found. Trends Electron. Des. Autom. 3.1 (Jan. 2009), pp. 1–95. ISSN: 1551-
3939. DOI: 10 . 1561 / 1000000013. URL: http : / / dx . doi . org / 10 . 1561 /
1000000013.
[FP09] L. Ferro and L. Pierre. “ISIS: Runtime verification of TLM platforms”. In: Speci-
fication Design Languages, 2009. FDL 2009. Forum on. Sept. 2009, pp. 1–6.
[GD10] D. Große and R. Drechsler. Quality-Driven SystemC Design. Springer, 2010.
[GLD10] D. Große, H. Le, and R. Drechsler. “Proving transaction and system-level proper-
ties of untimed SystemC TLM designs”. In: MEMOCODE. 2010, pp. 113–122.
[God91] Patrice Godefroid. “Using Partial Orders to Improve Automatic Verification Meth-
ods”. In: Proceedings of the 2Nd International Workshop on Computer Aided Ver-
ification. CAV ’90. London, UK, UK: Springer-Verlag, 1991, pp. 176–185. ISBN:
3-540-54477-1. URL: http : / / dl . acm . org / citation . cfm ? id = 647759 .
735044.
[God96] P. Godefroid. Partial-Order Methods for the Verification of Concurrent Systems:
An Approach to the State-Explosion Problem. Ed. by J. van Leeuwen, J. Hart-
manis, and G. Goos. Secaucus, NJ, USA: Springer-Verlag New York, Inc., 1996.
ISBN : 3540607617.
[GP93] Patrice Godefroid and Didier Pirottin. “Refining Dependencies Improves Partial-
Order Verification Methods (Extended Abstract)”. In: Proceedings of the 5th In-
ternational Conference on Computer Aided Verification. CAV ’93. London, UK,
UK: Springer-Verlag, 1993, pp. 438–449. ISBN: 3-540-56922-7. URL: http://
dl.acm.org/citation.cfm?id=647762.735513.
[Gro02] T. Grotker. System Design with SystemC. Norwell, MA, USA: Kluwer Academic
Publishers, 2002. ISBN: 1402070721.
[GW93] Patrice Godefroid and Pierre Wolper. “Using Partial Orders for the Efficient Verifi-
cation of Deadlock Freedom and Safety Properties”. In: Form. Methods Syst. Des.
2.2 (Apr. 1993), pp. 149–164. ISSN: 0925-9856. DOI: 10 . 1007 / BF01383879.
URL : http://dx.doi.org/10.1007/BF01383879.
[Hae+11] F. Haedicke et al. “metaSMT: Focus on Your Application not on Solver Integra-
tion”. In: DIFTS. 2011, pp. 22–29.
160 Bibliography
[KEP06] Daniel Karlsson, Petru Eles, and Zebo Peng. “Formal Verification of Systemc De-
signs Using a Petri-net Based Representation”. In: Proceedings of the Conference
on Design, Automation and Test in Europe: Proceedings. DATE ’06. Munich, Ger-
many: European Design and Automation Association, 2006, pp. 1228–1233. ISBN:
3-9810801-0-6. URL: http : / / dl . acm . org / citation . cfm ? id = 1131481 .
1131824.
[KGG08] Sudipta Kundu, Malay Ganai, and Rajesh Gupta. “Partial Order Reduction for
Scalable Testing of systemC TLM Designs”. In: Proceedings of the 45th An-
nual Design Automation Conference. DAC ’08. Anaheim, California: ACM, 2008,
pp. 936–941. ISBN: 978-1-60558-115-6. DOI: 10.1145/1391469.1391706. URL:
http://doi.acm.org/10.1145/1391469.1391706.
[Kin76] James C. King. “Symbolic Execution and Program Testing”. In: Commun. ACM
19.7 (July 1976), pp. 385–394. ISSN: 0001-0782. DOI: 10.1145/360248.360252.
URL : http://doi.acm.org/10.1145/360248.360252.
Bibliography 161
[Tra+07] Claus Traulsen et al. “A systemC/TLM Semantics in PROMELA and Its Possi-
ble Applications”. In: Proceedings of the 14th International SPIN Conference on
Model Checking Software. Berlin, Germany: Springer-Verlag, 2007, pp. 204–222.
ISBN : 978-3-540-73369-0. URL: http : / / dl . acm . org / citation . cfm ? id =
1770532.1770552.
[TV10] D. Tabakov and M.Y. Vardi. “Monitoring temporal SystemC properties”. In: For-
mal Methods and Models for Codesign (MEMOCODE), 2010 8th IEEE/ACM In-
ternational Conference on. July 2010, pp. 123–132. DOI: 10 . 1109 / MEMCOD .
2010.5558640.
[Val89] Antti Valmari. “Stubborn Sets for Reduced State Space Generation”. In: Proceed-
ings of the Tenth International Conference on Application and Theory of Petri
Nets. 1989, pp. 1–22.
[Val91] Antti Valmari. “A Stubborn Attack On State Explosion”. In: Proceedings of the
2Nd International Workshop on Computer Aided Verification. CAV ’90. London,
UK, UK: Springer-Verlag, 1991, pp. 156–165. ISBN: 3-540-54477-1. URL: http:
//dl.acm.org/citation.cfm?id=647759.735025.
[Val98] Antti Valmari. “The state explosion problem”. English. In: Lectures on Petri Nets
I: Basic Models. Ed. by Wolfgang Reisig and Grzegorz Rozenberg. Vol. 1491.
Lecture Notes in Computer Science. Springer Berlin Heidelberg, 1998, pp. 429–
528. ISBN: 978-3-540-65306-6. DOI: 10 . 1007 / 3 - 540 - 65306 - 6 _ 21. URL:
http://dx.doi.org/10.1007/3-540-65306-6_21.
[Var07] Moshe Y. Vardi. “Formal Techniques for SystemC Verification”. In: Proceedings
of the 44th Annual Design Automation Conference. DAC ’07. San Diego, Cal-
ifornia: ACM, 2007, pp. 188–192. ISBN: 978-1-59593-627-1. DOI: 10 . 1145 /
1278480.1278527. URL: http://doi.acm.org/10.1145/1278480.1278527.
[VV98] Kimmo Varpaaniemi and Kimmo Varpaaniemi. On the Stubborn Set Method in
Reduced State Space Generation. Tech. rep. 1998.
[Yan+07] Yu Yang et al. “Distributed Dynamic Partial Order Reduction based Verification of
Threaded Software”. In: In Workshop on Model Checking Software (SPIN 2007.
2007.
[Yan+08] Yu Yang et al. “Efficient Stateful Dynamic Partial Order Reduction”. In: Proceed-
ings of the 15th International Workshop on Model Checking Software. SPIN ’08.
Los Angeles, CA, USA: Springer-Verlag, 2008, pp. 288–305. ISBN: 978-3-540-
85113-4. DOI: 10.1007/978-3-540-85114-1_20. URL: http://dx.doi.org/
10.1007/978-3-540-85114-1_20.
[YWY06] Xiaodong Yi, Ji Wang, and Xuejun Yang. “Stateful Dynamic Partial-Order Re-
duction”. English. In: Formal Methods and Software Engineering. Ed. by Zhim-
ing Liu and Jifeng He. Vol. 4260. Lecture Notes in Computer Science. Springer
Berlin Heidelberg, 2006, pp. 149–167. ISBN: 978-3-540-47460-9. DOI: 10.1007/
11901433_9. URL: http://dx.doi.org/10.1007/11901433_9.