1 Introduction

In a uniprocessor machine with a non-optimizing compiler, the semantics of a concurrent program is given by the set of interleavings the memory accesses of its constituent threads, a model which is known as sequential consistency (SC) [15]. In multiprocessor machines and/or with optimizing compilers, however, more behaviors are possible; they are formally described by what is known as a weak memory model. Simple examples of such “weak” behaviors are in the SB (store buffering) and LB (load buffering) programs below:

figure a

Assuming all variables are 0 initially, the weak behaviours in question are the ones in which a and b have the values mentioned in the program comments. In the SB program on the left this behaviour is allowed by all existing weak memory models, and can be easily explained in terms of reordering: the hardware may execute the independent store to x and load from y in reverse order. Similarly, the behaviour in the LB program on the right, which is allowed by some memory models, can be explained by reordering the load from x and the subsequent store to y. This explanation remains the same whether the hardware itself performs out-of-order execution, or the compiler, as a part of its optimisation passes, performs these transformations, and the hardware runs a reordered program.

In this paper, we will address two questions:

  1. 1.

    How do such non-SC behaviours affect existing techniques for verifying concurrent programs?

  2. 2.

    How can we verify concurrent programs in spite of weak memory behaviours?

For the first question, we will note that even rather basic proof methods for SC concurrency are unsound under weak memory. Specifically, in Sect. 2, we will show that this is the case for the Owicki-Gries (OG) proof method [21].

To answer the second question, there are two main approaches. One approach is to determine a class of programs for which weak memory consistency does not affect their correctness. One such a class of programs are data-race-free (DRF) programs, namely programs that under SC semantics have no concurrent conflicting accesses (two accesses to the same location, at least one of which a write). Ensuring that a memory model ascribes only SC behaviours to DRF programs has become a standard sanity requirement for weak memory models [1]. For specific memory models, one can develop larger classes of programs, whose behaviour is unaffected by the weak memory consistency (e.g., [3, 12, 20]).

An alternative approach is to develop proof techniques for reasoning directly about programs under a certain weak memory model. To do so, we usually take an existing proof technique that has been developed for SC concurrency and adapt it to make it sound under a specific weak memory model. We may then further extend the method to make the proof technique more useful for reasoning about specific weak memory features. As an example of this approach, in [13], we applied it to the OG proof method by weakening OG’s non-interference check to restore its soundness under release-acquire (RA) consistency.

In this paper, we will focus on this latter approach, but apply it to concurrent separation logic (CSL) [19]. Compared with OG, CSL is much better suited for reasoning under weak memory consistency, because by default it can reason only about DRF programs. As such, it is trivially sound under weak memory. We will then gradually extend CSL with features suitable for reasoning about the various synchronisation primitives provided by C11, and conclude with a discussion of some remaining challenges. In order to keep the exposition as simple as possible, I will elide inessential technical details and not discuss the soundness proofs of the presented proof rules. The missing details can be found in [5, 6, 25, 27, 28].

2 Owicki-Gries is Unsound Under Weak Memory!

To motivate why developing program logics for weak memory consistency is non-trivial, we start by showing that the Owicki-Gries (OG) system is unsound.

In 1976, Owicki and Gries [21] introduced a proof system for reasoning about concurrent programs, which formed the basis of rely/guarantee reasoning. Their system includes the usual Hoare logic rules for sequential programs, a rule for introducing auxiliary variables, and the following parallel composition rule:

$$\begin{aligned} \frac{\left\{ \begin{array}{@{}l@{}}P_1\end{array}\right\} c_1 \left\{ \begin{array}{@{}l@{}}Q_1\end{array}\right\} \quad \left\{ \begin{array}{@{}l@{}}P_2\end{array}\right\} c_2 \left\{ \begin{array}{@{}l@{}}Q_2\end{array}\right\} \quad {\text {the two proofs are non-interfering}}}{\left\{ \begin{array}{@{}l@{}}P_1 \wedge P_2\end{array}\right\} c_1 \parallel c_2 \left\{ \begin{array}{@{}l@{}}Q_1 \wedge Q_2\end{array}\right\} } \end{aligned}$$

This rule allows one to compose two verified programs into a verified concurrent program that assumes both preconditions and ensures both postconditions. The soundness of this rule requires that the two proofs are non-interfering, namely that every assertion R in the one proof is stable under any \(\{P\}x:=e\) (guarded) assignment in the other and vice versa; i.e., for every such pair, \(R \wedge P \vdash R[e/x]\).

The OG system relies quite heavily on sequential consistency. In fact, OG is complete for verifying concurrent programs under SC [22], and is therefore unsound under any weakly consistent memory semantics. Auxiliary variables are instrumental in achieving completeness—without them, OG is blatantly incomplete; e.g., it cannot verify that \(\left\{ \begin{array}{@{}l@{}}x=0\end{array}\right\} x := x+1 \parallel x := x+1 \left\{ \begin{array}{@{}l@{}}x=2\end{array}\right\} \) where “\(:=\)” denotes atomic assignment.

Nevertheless, many useful OG proofs do not use auxiliary variables, and one might wonder whether such proofs are sound under weak memory models. This is sadly not the case. Figure 1 presents an OG proof that the SB program cannot return \(a=b=0\) whereas under all known weak memory models it can in fact do so. Intuitively speaking, the proof is invalid under weak memory because the two threads may have different views of memory before executing each command. Thus, when thread II terminates, thread I may perform \(a:=y\) reading \(y=0\) and storing 0 in a, thereby invalidating thread II’s last assertion.

Fig. 1.
figure 1

OG proof that SB cannot return \(a = b = 0\).

3 RC11 Preliminaries

For concreteness, we will now introduce a simple programming language containing all the features of RC11, the rectified version of the C/C++11 memory model due to Lahav et al. [14]. Programs are given by the following grammar:

figure b

Expressions, e, are built out of program variables, constants and arithmetic and logical operators. Commands, c, contain the empty command, sequential and parallel composition, conditionals and loops, assignments to local variables, memory accesses (loads, stores, und compare and swap), allocation, and fences.

Memory accesses are annotated with an access mode, o, which indicates the level of consistency guarantees provided by the access, which in turn determines its implementation cost.

The weakest access mode is non-atomic (), which is intended for normal data loads and stores. Races on non-atomic accesses are treated as program errors: it is the responsibility of the programmer to ensure that such races never occur. The remaining access modes are intended for synchronisation between threads and, as such, allow races. The strongest and most expensive mode are sequentially consistent () accesses, whose primary purpose is to restore the simple interleaving semantics of sequential consistency [15] if a program (when executed under SC semantics) has races only on SC accesses. Weaker than SC atomics are acquire () loads and release () stores,Footnote 1 which can be used to perform “message passing” between threads without incurring the implementation cost of a full SC access; and weaker and cheaper still are relaxed () accesses, which provide only minimal synchronisation guarantees.

RC11 also supports language-level fence instructions, which provide finer-grained control over where hardware fences are to be placed and can be used in conjunction with relaxed accesses to synchronise between threads. Fences are also annotated with an access mode, .

We will discuss the semantics of these access modes and fences in more detail as we introduce program logic rules to reason about them.

4 Reasoning About Non-atomic Accesses Using CSL

We start with non-atomics, which have to be accessed in a data-race-free (DRF) fashion. To reason about them, it is natural to consider O’Hearn’s concurrent separation logic (CSL) [19], because it rules out data races by construction. In CSL, accessing a memory location, \(\ell \), requires the command to have the permission to access that location in its precondition in the form of a points-to assertion, \(\ell \mapsto v\). This formula asserts that the memory at location \(\ell \) stores the value v, moreover it gives permission to the bearer of this assertion to access and possibly modify the contents of memory at location \(\ell \). Formally, the permission is generated by the allocation rule and required in the preconditions of the load and store rules.

figure c

The load rule further asserts that the value read is the one recorded in the points-to assertion, while the store rule allows one to update this value.

Fig. 2.
figure 2

Proof rules of CSL (without resource invariants).

The other CSL rules are listed in Fig. 2: these include the standard rules from Hoare logic (skip, assign, seq, if, while), the parallel composition rule (par), the consequence rule (conseq), the disjunction and existential elimination rules (disj, ex), and the frame rule (frame). In our presentation of the rules, we exclude any mention of “resource invariants” and the rules for dealing with them, as we will not ever directly use this feature of the logic. In preparation for the extensions in the next section, our formulation of the consequence rule uses ghost implication (\(\Rrightarrow \)) instead of normal logical implication. Ghost implication is a generalisation of normal implication that in addition allows frame-preserving updates to any ghost resources mentioned in the assertions.

CSL’s parallel composition rule requires the preconditions of the two threads to be disjoint (i.e., \(P_1 * P_2\)), which (together with the load and store rules) precludes the two threads of accessing the same location simultaneously. The disjointness conditions of the rule check that each thread does not modify any of the variables appearing in the other thread’s program or specification.

5 RSL: Reasoning About Release-Acquire Synchronisation

Next, let us consider C11’s acquire loads and release stores, whose main mode of use is to establish synchronisation between two threads. The basic synchronisation pattern is illustrated by the following “message passing” idiom:

figure d

Here, assuming that initially \([x]=[y]=0\), the program cannot read \(a=1\) and \(b=0\). According to C11, when an acquire load reads from a release store, this results in a synchronisation. As a result, any memory access happening before the release store (by being performed previously either by the same thread or by some previously-synchronising thread) also happens before the acquire load and any access happening after it. In the MP program, this means that the write happens before the load, and thus the reading thread must return \(b=1\) in the case it read \(a=1\).

To reason about release and acquire accesses, Vafeiadis and Narayan [28] introduced relaxed separation logic (RSL), which extends CSL assertions with two new assertion forms:

figure e

These represent the permission to perform a release store or an acquire load respectively, and attach to location \(\ell \) a mapping \(\mathcal {Q}\) from values to assertions. This mapping describes the manner in which the location \(\ell \) is used by the program. We can roughly consider it as an invariant stating: “if location \(\ell \) holds value v, then the assertion \(\mathcal {Q}(v)\) is true.”

At any point in time, a non-atomic location may be converted into an atomic location with the following ghost implication:

figure f

In the antecedent of the ghost move, the invariant should hold for the value of the location; as a result, we get the permissions to write and read that location.

RSL’s release write rule

figure g

says that in order to do a release write of value v to location \(\ell \), we need to have a permission to do so, , and we have to satisfy the invariant specified by that permission, namely \(\mathcal {Q}(v)\). After the write is done, we no longer own the resources specified by the invariant (so that readers can obtain them).

The acquire read rule

figure h

complements the release write rule. To perform an acquire read of location \(\ell \), one must have an acquire permission for \(\ell \). Just as with a release permission, an acquire permission carries a mapping \(\mathcal {Q}\) from values to assertions. In case of an acquire permission, this mapping describes what resource will be acquired by reading a certain value; so if the value v is read, resource \(\mathcal {Q}(v)\) is acquired.

This rule is slightly complicated by a technical detail. In the postcondition, we cannot simply retain the full acquire permission for location \(\ell \), because that would enable us to read the location again and acquire the ownership of \(\mathcal {Q}(v)\) a second time. To prevent this, the acquire permission’s mapping in the postcondition becomes .

As a simple application of these rules, Fig. 3 shows a slightly abbreviated proof of the MP program. Initially, the rule mk-atom is applied to set up the invariant for location y. By the parallel composition rule, the first thread receives the permission to access x (specifically, \(x \mapsto 0\)) and the release write permission to y, which it uses to transfer away the \(x\mapsto 1\) resource. The second thread starts with the acquire read permission and uses it to get hold of the invariant of y, which, in the case that \(a\ne 0\), gives enough permission to the thread to access x non-atomically and establish \(b=1\). In the proof outline, we often use the consequence rule to forget permissions that are no longer relevant.

Fig. 3.
figure 3

Proof outline of MP using the invariant \(\mathcal {Q}(v) \triangleq (v = 0 \vee x \mapsto 1)\).

To allow multiple concurrent readers and writers, RSL’s write permissions are duplicable, whereas its read permissions are splittable as follows:

figure i

The reason why read permissions cannot simply be duplicated is the same as why the read permission is modified in the postcondition of the r-acq rule. If read permissions were made duplicable, then multiple readers would incorrectly be able to acquire ownership of the same resource.

6 FSL: Reasoning About Relaxed Accesses and Fences

Next, let us consider relaxed accesses. Unlike release stores and acquire loads, relaxed accesses do not synchronise on their own, but only when used together with release/acquire fences. Consider the following variant of the MP example using relaxed accesses and fences.

figure j

Like MP, MP-fences also satisfies the postcondition, \(a = 0 \vee b = 1\) (and so do the variants where either thread is replaced by the corresponding thread of MP), but if we remove any of the fences, the program will have undefined behaviour. (The reason for the latter is that in the absence of synchronisation, the non-atomic x-accesses are racy.)

In essence, we can think of resource transfer in the following way. When releasing a resource by a combination of a release fence and a relaxed write, at the fence we should decide what is going to be released, and not use that resource until we send it away by doing the write. Conversely, when acquiring a resource using a relaxed read together with an acquire fence, once we do the read, we know which resources we are going to get, but we will not be able to use those resources until we reach the synchronisation point marked by the acquire fence.

To formally represent this intuition, fenced separation logic (FSL) [5] introduces two modalities into RSL’s assertion language:

figure k

We use to mark the resources that have been prepared to be released, and to mark those waiting for an acquire fence. We require the invariants appearing in and permissions to contain no modalities, a condition called normalisability in [5]. In essence, these modalities are meant to appear only in the proof outlines of individual threads and to never be nested.

FSL supports all the rules we have seen so far. In addition, it has rules for relaxed accesses and fences. The rule for relaxed writes is almost exactly the same as w-rel.

figure l

As in w-rel, we have to have a write permission as well as the resource specified by its attached invariant. The only additional requirement is that the latter resource has to be under the modality stating that it can be released by a relaxed write. As we will later see, this ensures that any writes transferring away non-empty resources are placed after a release fence.

Similarly, the rule for relaxed reads differs from r-acq only in a single modality appearance:

figure m

While after acquire read, we gain ownership of the resource described by the permission, in the case of a relaxed read, we get the same resource under the modality. This makes the resource unusable before we reach an acquire fence.

The fence rules simply manage the two modalities as follows:

figure n

Release fences protect resources that are to be released by putting them under the modality, while acquire fences clear the modality making resources under it usable.

Figure 4 shows a proof outline of MP-fences using the rules presented in this section. Except for the treatment modalities, the proof itself essentially identical to that of MP. In the first thread, we use a combination of f-rel and the frame rule to put only \(x\mapsto 1\) under the modality. In the second thread, after the relaxed load, we use the consequence rule to forget the unnecessary permission and push the modality under the disjunction.

Fig. 4.
figure 4

Proof outline of MP-fences using the invariant \(\mathcal {Q}(v) \triangleq (v = 0 \vee x \mapsto 1)\).

7 Reasoning About Read-Modify-Write Instructions

Next, consider compare-and-swap, which is a typical example of a read-modify-write (RMW) instruction. reads the location \(\ell \) and if its value is v, it updates it atomically to \(v'\). If reads some value other than v, then the update is not performed. In either case, returns the value read. The tells us the type of event generated by a successful operation.

To reason about , we introduce a new type of assertion:

figure o

which denotes the permission to perform a on location \(\ell \). As with the and assertions, it records a mapping from values to assertions, which governs the transfer of resources via a operation.

The permission is obtained in a similar fashion as the and permissions. At any point in time, a non-atomic location may be converted into an atomic location with the following ghost implication:

figure p

The update permission is duplicable, and interacts with the and permissions, allowing us to perform not only updates, but also reads and writes, when holding an update permission.

figure q

According to uw-split, when holding the , we also have , allowing us to write to \(\ell \) using the appropriate atomic write rule. On the other hand, ur-split tells us that we are allowed to read when holding the permission, but we cannot gain any ownership (more precisely, no matter the value read, the acquired resource will always be the empty resource \(\mathsf {emp}\)).

We next consider the following rule for the acquire-release .Footnote 2

figure r

In the precondition, we have permission to perform the and some further resource, P, to be transferred away if the succeeds.

If the succeeds, we have at our disposal the resource \(\mathcal {Q}(v)\), which is split into two parts, A, and T. Resource A is the part that we are going to acquire and keep it for ourselves in the postcondition. Resource T will remain in the invariant \(\mathcal {Q}\). The second premise requires that the resource P (which we have in our precondition) together with the resource T (which we left behind when acquiring ownership) are enough to satisfy \(\mathcal {Q}(v')\), thus reestablishing the invariant for the newly written value. If, in addition to merely reestablishing the invariant, we manage to prove some additional facts, \(\varphi \), we can carry those facts into the postcondition. It is required, however, for these facts to be pure, meaning that the assertion \(\varphi \) is a logical fact and does not say anything about the ownership of resources or the state of the heap.

If the fails, then no resource transfer occurs, and the postcondition contains the same resources as the precondition.

Fig. 5.
figure 5

Rules for the other kinds of CAS weaker than . All of these rules implicitly have the same premises as the CAS-AR rule.

The rules for the other types of accesses are slight modifications of the cas-ar rule in the same vein as the ones that get us from r-acq and w-rel to r-rlx and w-rlx (see Fig. 5). Namely, wherever the access type is relaxed, and modalities are introduce to ensure a proper fence placement. Since the premises in these rules are the same as in cas-ar, we avoid repeating them.

  • A release is treated as a release write and a relaxed read. Therefore, in cas-rel sends away P without any restrictions, but the acquired resource, A, is placed under the modality, requiring the program to perform a acquire fence before accessing the resource.

  • Conversely, for an acquire , the resource to be transferred away is under the modality requiring a release fence before the , while the resource acquired is immediately usable.

  • A relaxed is relaxed as both read and write. This is reflected in the cas-rlx rule by having both modalities in play.

Fig. 6.
figure 6

Lock library verification using \(\mathcal {Q}(v) \triangleq (J \vee v \ne 0)\) and .

Finally, Fig. 6 presents a proof outline for verifying a spinlock implementation as an example of using the rules. In these proof outlines, the use of the consequence rule is left implicit. Specifically, in mk-lock, we apply the mk-atom-u rule to generate the update permission; in acquire-lock, we apply the ur-split rule to generate a read permission, while in release-lock, we apply the uw-split rule to generate a write permission.

8 GPS: Adding Protocols

The assertions so far have attached an invariant, \(\mathcal {Q}\), to each location that is meant to be used atomically. While such simple invariants suffice for reasoning about simple ownership transfer patterns, on their own they are too weak for establishing even basic coherence properties. Consider, for example, the following program, where initially \([x]=0\).

figure s

Although RC11 ensures that \(a \le b\) in every execution of this program, it is not possible to establish this postcondition with the separation logic rules we have seen thus far. To achieve this, we need a more expressive logic incorporating some limited form of rely-guarantee reasoning (e.g., as already available in OG).

A convenient way to support such reasoning has emerged in the context of program logics for SC concurrency, such as CAP [4], CaReSL [26], TaDA [23], and Iris [9], in the form of protocols. The idea is to attach to each atomic location an acyclic state transition system describing the ways in which the value of the location can be updated, and to have assertions talk about the current state of a location’s protocol. Formally a protocol, \(\tau \), is a tuple \({\langle {\varSigma _\tau , \sqsubseteq _\tau , \mathcal {Q}_\tau }\rangle }\), where \(\varSigma _\tau \) is the (non-empty) set of protocol states, \({\sqsubseteq }_\tau \) is a partial order on \(\varSigma _\tau \) relating a state to its possible future states, and \(\mathcal {Q}_\tau \) is a mapping from protocol states and values to assertions, attaching an invariant about the value of the location to each protocol state.

We extend the language of assertions with two new assertion forms:

figure t

which assert that \(\ell \) is governed by the protocol \(\tau \) and its current state is reachable from the state s. represents an exclusive write permission to the protocol, whereas is a duplicable read permission. As usual, these permissions can be generated from a points-to assertion with a ghost move.

figure u

Consider the following two simplified proof rules for relaxed reads and writes.

figure v

To perform a relaxed write, the thread must own the exclusive write permission for that location; it then has to chose a future state \(s'\) of the current state and establish the invariant of that state. Since it is a relaxed write, no ownership transfer is possible (at least without fences). So, in this somewhat simplified rule, we require \(\mathcal {Q}_\tau (s',v)\) to hold of the empty heap.

Conversely, to perform a relaxed read, the thread must own a shared read permission for that location stating that it is at least in state s. It then knows that the location is in some future protocol state \(s'\) of s, and gets to know that the invariant of \(\mathcal {Q}_\tau \) holds for that state and the value that it read. Since the read is relaxed, to avoid incorrect ownership transfers, the postcondition gets only the pure part of this invariant.

Fig. 7.
figure 7

Proof outline of COH using the protocol \({\langle {\{0,1,2\},\le ,\lambda (s,v).\,s=v}\rangle }\).

These rules can be extended to use the FSL modalities to allow ownership transfer in combination with fences, but even these basic rules are sufficient for verifying the COH example. Returning to the example, we take as the protocol \(\tau \) of x to consist of three states ordered linearly (\(0 \sqsubseteq _\tau 1 \sqsubseteq _\tau 2\)), each saying that x has the respective value. Pictorially, we have:

figure w

The proof outline for COH is rather straightforward and is shown in Fig. 7. In the writer thread, each write moves to the next state. In turn, the reader can assert that each read gets a value greater or equal to the last state it observed.

Besides protocols, GPS also introduced ghost state in the form of ghost resources and escrows/exchanges. These features enable GPS to support ownership transfer over release-acquire synchronization. For an explanation of these features, we refer the reader to [10, 25, 27].

A Note About the Different Versions of GPS. GPS was initially developed by Turon et al. [27] for a fragment of the programming language of Sect. 3 containing only non-atomic and release/acquire accesses. It was later extended by Tassarotti et al. [25] with “exchanges” and used to verify a version of the RCU algorithm. Later, Kaiser et al. [10] developed a slight variant of GPS within the Iris framework featuring a simpler “single writer” rule. All these three works had their soundness proofs verified in Coq, but cannot handle relaxed accesses. In a different line of work, He et al. [7] have extended GPS to also cover relaxed accesses albeit without a mechanised soundness proof.

9 Conclusion: Challenges Ahead

In this section, we will review three main challenges in this line of work. The first two have to do with the soundness of the presented logic, while the third has to do with their practical usage.

9.1 Soundness Under Weaker Memory Models

All the program logics discussed so far have been proved sound with respect to the RC11 weak memory model [14], which forbids load-store reordering for atomic accesses. Reordering a relaxed-atomic load past a later relaxed-atomic store, however, is allowed in some weaker memory models, such as the “promising” model of Kang et al. [11], as it is key to explaining the weak behaviour of the LB example from the introduction.

Fig. 8.
figure 8

FSL proof outline of LB+dep where \(\mathcal {Q}(v) \triangleq v = 0 \vee z \mapsto 0\).

Extending the soundness of these logics to weaker models permitting the weak behaviour of LB is rather challenging. In fact, FSL with its current model of assertions (not discussed in this paper but presented in [5]) is unsound under such models as shown by the proof outline in Fig. 8.

Under the assumption that is unsatisfiable (used in the middle of thread I to deduce that ), the proof establishes that \(a = 0\), whereas the program in question may clearly yield \(a=1\) if the load and the store of thread I are reordered. Although is unsatisfiable in the current model of assertions, it is quite possible to devise a different model of assertions, according to which the aforementioned assertion is satisfiable, and thus potentially restore the soundness of FSL under some weaker memory models.

9.2 Reasoning About SC Accesses and Fences

As the reader will have noticed, in this paper we have not presented any rules for reasoning about atomics. Naturally, since atomics are stronger than the release/acquire ones, the presented release/acquire rules are also sound for accesses and fences. The question is whether we can get any stronger proof rules for accesses and fences.

For fences, it seems quite likely that we can get better rules. An fence can be thought of as a combination of a fence and an RMW over a ghost location. Therefore, we should be able to extend the Hoare triples with a global invariant, J, which can be accessed at fences:

figure x

Such an invariant may also be used for providing rules for accesses. The fragment of RC11 restricted to accesses only of or kind corresponds exactly to the language targeted by CSL [19] (by treating accesses as being surrounded by atomic blocks). Thus, for this fragment at least, one can easily derive sound rules for accesses from the CSL rules involving resource invariants. The open question is whether one can extend the soundness of such rules to the full RC11 model, especially in cases where the same location may accessed both using and non- accesses.

9.3 Tool Support

The soundness proofs of the aforementioned adaptations of separation logic have all been mechanised in the Coq proof assistant (see RSL [28], FSL [5], GPS [27], FSL++ [6]) together with some example proofs. Nevertheless, doing proofs in those program logics in Coq without any additional infrastructure is quite cumbersome. What is very much needed is some support for more automated proofs.

Such support already exists for various flavours of (concurrent) separation logic. There exist a wide range of tools, from fully automated ones for suitable fragments of the logic to tactic libraries for assisting the manual derivation of mechanised proofs (e.g., [2, 8, 9, 16,17,18]). For the work described here, the two most relevant tools are probably Viper [17] and the Iris framework [9].

Viper [17] is a generic program verifier for resource-based logics. Recently, Summers and Müller [24] have encoded versions of the RSL/FSL proof rules into Viper and have used them to verify among other examples a slightly simplified version of the ARC library verified in [6]. While their encoding is not expressive enough to verify the actual ARC implementation, it is much more convenient to use for the programs falling in its domain than the FSL’s Coq formalisation.

Iris [9] is a generic logical framework built around a higher-order variant of separation logic. It is deeply embedded in Coq and comes with a useful set of Coq tactics for doing proofs in that framework. In recent work, Kaiser et al. [10] have encoded a slight variant of GPS into Iris (thereby reproving its soundness within Iris) and have used Iris’s infrastructure to get a convenient way of constructing GPS proofs in Coq.