A Proof Recipe for Linearizability in Relaxed Memory Separation Logic

Linearizability is the de facto standard for correctness of concurrent objects—it essentially says that all the object’s operations behave as if they were atomic. There have been a number of recent advances in developing increasingly strong linearizability specifications for relaxed memory consistency (RMC), but scalable proof methods for these specifications do not exist due to the challenges arising from out-of-order executions (requiring event reordering) and selected synchronization (requiring tracking of view transfers). We propose a proof recipe for the linearizable history specifications by Dang et al. in the Iris-based iRC11 concurrent separation logic in Coq. Key to our proof recipe is the notion of object modification order (OMO), which generalizes the modification order of the C11 memory model to an object-local setting. Using OMO we minimize the conditions that need to be proved for event reordering. To enable proof reuse for concurrent libraries that are built on top of others, OMO provides the novel notion of a commit-with relation that connects the linearization points of the lower and upper libraries. Using our recipe, we verify the linearizability of the Michael–Scott queue, the elimination stack, and Folly’s MPMC queue in RMC for the first time; and verify stronger specifications of a spinlock and atomic reference counting in RMC than prior work.


INTRODUCTION
Linearizability [Herlihy and Wing 1990] is the de facto standard specification for concurrent objects.It requires that (1) each operation on the object appears to take effect instantaneously at some point during its execution, called its linearization point; and (2) the linearization points of all operations form a sequential history according to their execution order, called the linearization order, that adheres to the object's sequential specification.Linearizability of various data structures has been verified in the sequential consistency (SC) memory model, where all threads take turns to execute instructions on the latest state of the shared memory.
Linearizability gets complicated in relaxed memory consistency (RMC) models.Relaxed behaviors, such as instruction reordering, make the linearization order deviate from the execution order.Fig. 1 illustrates the complexities using Treiber [1986]'s stack.The global variable d is initialized with 0 and the stack s is initially empty.Assume the left thread ( ) executes first, storing 42 to d (line L1) and pushing 1 to s (line L2).The right thread ( ) may fail to pop a value from s (line R1) as the push event may not be visible for .This greatly contrasts with the SC model where every event is immediately visible to all threads, and R1 thus cannot miss the push.Assume the event becomes visible to after R1, then the pop (line R2) will succeed.This execution illustrates the following additional desired properties of the linearization order in RMC over those in the SC model: • Sequential specification with event reordering: While the execution order of the stack events is L2, R1, and R2, their linearization order should be R1, L2, and R2 to observe the sequential specification of the stack: R1 (empty pop) should be linearized before L2 (push) to make sense.We call such a divergence of the linearization order from the execution order event reordering.• Causal consistency: Despite event reordering, the linearization order should observe causality of events.For instance, L2, R2, and R1 (while satisfying the stack's sequential specification) is not a valid linearization order as it does not preserve the causally ordered events R1 and R2 that are sequentially executed in the same thread.• Selective synchronization: should load 42 from d (not the initial value 0) because the matching push (L2) and pop (R2) events synchronize in RMC, i.e., all events observed in before L2 should be visible by after R2.But the synchronization is selective, e.g., the non-matching push (L2) and empty pop (R1) events do not synchronize even though they are linearized.
State of the art.Various strong specifications that capture linearizability in RMC have been proposed [Batty et al. 2013;Dang et al. 2022;Dongol et al. 2018;Mével and Jourdan 2021;Mével et al. 2020;Raad et al. 2019;Singh and Lahav 2023;Smith et al. 2020].We focus on specifications in concurrent separation logic [Brookes 2004;O'Hearn 2004] because they provide strong support for compositional verification and mechanization in a proof assistant.For the SC model, linearizability is commonly encoded through logically atomic triples (LATs) [da Rocha Pinto et al. 2014], which are supported by the Iris framework for higher-order concurrent separation logic in Coq [Jung et al. 2016[Jung et al. , 2018[Jung et al. , 2015;;Krebbers et al. 2018Krebbers et al. , 2017a,b],b].Linearizability of the push operation of the SC version of Treiber's stack is encoded by ⟨vs.Stack( , vs)⟩ push( , ) ⟨Stack( , :: vs)⟩.The representation predicate Stack( , vs) asserts that a stack located at has abstract state vs ∈ List(Val).
The LAT expresses that push( , ) appears to transform the stack atomically into a stack with an additional value at some atomic instruction during push's execution, which is the linearization point.The encoding using LATs is canonical in that it coincides with the standard definition of linearizability in SC [Birkedal et al. 2021].Mével and Jourdan [2021]; Mével et al. [2020] first use LATs to encode linearizability in RMC in the context of the Iris-based Cosmo logic for OCaml's memory model.Dang et al. [2022] show how to take this approach further, proposing linearizable history specifications (among others), in the context of the Iris-based iRC11 logic for ORC11 [Dang et al. 2020], a variant of the RC11 memory model [Lahav et al. 2017]. 1 A key difference from LATs in the SC model is the representation predicate.The Stack( , ) predicate no longer keeps track of a list of values, but it consists of the whole set of events that have ever been performed on the stack .Linearizability is represented as the existence of a linearization (total ordering) of that satisfies the desired properties.When an operation is executed, the LAT specification appears to transform Stack( , ) into Stack( , ⊎ { }) atomically, where is a new event and ⊎ is the disjoint set union of and .Dang et al. [2022] propose another style of specifications, called partial-order-based specifications, where Stack( , ) does not construct a concrete total order but only guarantees functional correctness (e.g., LIFO property) among events in .While the two styles of specifications can be equivalent in strength if partial-order-based specifications are sufficiently constrained, linearizable history specifications are more akin to the concept of linearizability and easier for clients to use thanks to the total ordering of events.As such, we focus on linearizable history specifications.
Problem.While Dang et al. [2022] show how to give strong linearizability specifications in relaxed memory separation logic, they did not detail how to verify these specifications.Moreover, for the linearizable history specifications, they verify Treiber's stack as the sole case study.This Coq proof involves a significant effort of 1,615 SLOC (significant lines, excluding comments and blank lines), which is 48 times the size of the implementation.In our experience of verifying the linearizable history specifications for other concurrent objects, we faced the following challenges: C1 Constructing the linearization order: Due to event reordering, correctness of the linearization order needs to be reproved for each event.This proof should be immediate in easy cases and manageable in difficult cases.For Treiber's stack, the linearization order is straightforward as it coincides with the modification order of the head pointer, but significant bookkeeping is required.§4).C3 Inferring the logical state: In the SC model, the logical state of an object invariant is often straightforward to infer from the physical state.In contrast, in RMC, the connection between the logical and physical states is much less obvious due to the possibilities of event reordering.This makes interactive and semi-automated proofs in proof assistants difficult.
Contributions.We propose a linearizability proof recipe for concurrent objects in relaxed memory separation logic that addresses the aforementioned verification challenges.
In §3, we propose a proof structure recipe to address C1 (constructing the linearization order).We observe that the linearization order usually evolves like the modification order (total coherence order of writes) of a shared location in the C11 memory model [Lahav et al. 2017].We capture this observation in a novel object modification order (OMO) separation logic predicate.OMO enforces the linearization order to evolve only by inserting new events into the existing order.We describe 154:4 Sunho Park, Jaewoo Kim, Ike Mulder, Jaehwang Jung, Janggun Lee, Robbert Krebbers, and Jeehoon Kang several proof rules for inserting events.These rules minimize the proof obligations needed to re-establish the desired properties of the linearization order.In §4, we propose a proof composition recipe to address C2 (compositional verification).We observe that the linearization point of an upper-level object event often coincides with that of its lower-level object event.We capture these relations using a novel commit-with relation inspired by the well-known simulation techniques used in proving behavior refinement [Brookes and Rounds 1983], and design proof rules that enable us to define upper-level object invariants without inspecting the lowerlevel object implementations.As a preliminary, we tweak Dang et al.'s specifications to expose the minimal necessary details on the events of the lower-level object, and recognize shared locations as primitive objects.
In §5, we propose a proof automation recipe to address C3 (inferring the logical state).We observe that the proof rules for our structure and composition recipes minimize proof obligations so that many of them can be discharged with pattern-based automation.We adapt the Diaframe [Mulder and Krebbers 2023;Mulder et al. 2022] separation logic automation framework in Iris to RMC, and apply it to our recipes to infer the logical state in a best-effort fashion.
In §6, we demonstrate that our recipe is effective on reducing verification efforts and facilitating new proofs for RMC, see Fig. 2. We verify linearizability of Treiber's stack with 71 SLOC of Coq proof (96% reduced from Dang et al.'s proof); those of the Michael-Scott queue [Michael and Scott 1996], the elimination stack [Hendler et al. 2004], and Folly's MPMC queue [Meta 2023] for the first time; and stronger specifications of a spinlock and atomic reference counting than prior work.All our results (including the recipe itself) are mechanized using the iRC11 relaxed memory separation logic, built in the Iris framework, atop the Coq proof assistant.
In §2, we review the background.In §7 and §8, we discuss related and future work, respectively.

View-based Operational Models for RMC
In the sequential consistency (SC) model, each thread is guaranteed to read the latest value written to each location.This is no longer the case for RMC models.To account for the weak semantics of hardware and compiler optimizations, these models formalize out-of-order executions.RMC models constrain the values that can be read through the notions of coherence and causality.There are primarily two styles of RMC models.In axiomatic models [Batty et al. 2011;Lahav et al. 2017] coherence and causality are expressed in terms of relations between memory events in the complete execution graph.In view-based operational models, such relations are implicitly modeled by each thread's behavior depending on their thread-local view of the memory state.We review operational models, which form the semantic basis for the separation logic we use in this paper.Our explanation is simplified for brevity; details can be found in Kang et al. [2017] and Dang et al. [2020].
Fig. 3 contains an example.The initial memory has messages with value 0 for locations ℓ and ℓ .If thread writes 42 to ℓ and 1 to ℓ , new messages with value 42 and 1 are added to the location histories of ℓ and ℓ , respectively.Subsequently, if thread is scheduled after thread , it may non-deterministically read 0 (stale) or 1 (latest) from ℓ .However, if thread reads 1 (latest) from ℓ , then the read from ℓ cannot result in a stale value, i.e., it surely is 42.That is because of the release-acquire synchronization through the release store in and acquire load in .
View-based RMC models use the following notions to constrain the values that are read: • Coherence.The coherence-before order from axiomatic models is modeled by considering the message history M (ℓ) as totally ordered-i.e., given timestamps 1 < 2 , the message M (ℓ, 1 ) is coherence-before M (ℓ, 2 ).The X-axis in Fig. 3 represents timestamps, and initial messages have the bottom timestamp (left corner).Timestamps of different locations are not comparable.• Causality.The happens-before order from axiomatic models is modeled through views.Each thread has a current view cur ∈ View = Loc ⇀ Time, which expresses that for each location ℓ the write of M (ℓ, cur (ℓ)) has been observed by that thread.The current view can only increase during execution-after performing an instruction, the new current view cur ′ should extend the prior current view cur , i.e., cur ⊑ cur ′ , which is defined as ∀ℓ.cur (ℓ) ≤ cur ′ (ℓ).
Semantics of relaxed (rlx) accesses.The semantics of a relaxed load and store operation are: (1) a load from ℓ reads a message at cur (ℓ) or later in M; (2) a store to ℓ writes a message at (strictly) later than cur (ℓ) in M; and (3) reading or writing a message M (ℓ, ) updates cur (ℓ) to .For the example in Fig. 3, the store of 42 to ℓ by thread updates its current view from 0 to 1 by incorporating the new message written to ℓ .
Semantics of release (rel)/acquire (acq) accesses.The semantics of a release store and an acquire load operation are similar to that of their relaxed counterparts.However, to account for releaseacquire synchronization it involves view passing from the sender of the message to the receiver.This is best demonstrated by the example in Fig. 3.When thread performs a release store to ℓ , it adds a message (1, 1 ) to the location history of ℓ (the written value and the updated current view).Later, when thread performs an acquire load and obtains that message, its view 1 is incorporated into thread 's current view, i.e., 1 = 0 ⊔ 1 , where the join 1 ⊔ 2 is defined as ℓ ↦ → max( 1 (ℓ), 2 (ℓ)).After synchronization, this ensures thread reads 42 (not 0) from ℓ .Note that synchronization happens only when a store is release and a load is acquire (or stronger).A relaxed store operation does not put the thread's current view in the new message, and a relaxed load operation does not incorporate the message view into the thread's current view.Semantics of non-atomic (na) accesses.Non-atomic accesses are weaker than relaxed accesses.A data race on a non-atomic access has undefined behavior, i.e., execution in the operational semantics gets stuck.Undefined behavior is modeled through a race detector [Dang et al. 2020, §5.1].Note that the relaxed store/load on ℓ in Fig. 3 can be replaced by a non-atomic store/load because the release-acquire synchronization prevents data races.

Separation Logic for RMC
We present the basic principles of the iRC11 relaxed memory separation logic [Dang et al. 2020], which extends the Iris separation logic [Jung et al. 2018] with support for the ORC11 RMC model.
The key difference between separation logics for SC (such as Iris) and separation logics for RMC (such as iRC11) is the necessity to account for out-of-order executions.Separation logics for SC assume that the resources of each thread and the global invariants are interpreted with respect to the latest memory state-but this principle is not sound in RMC.The resources of each thread should be interpreted with possibly stale memory states subject to the thread's current view.The iRC11 logic achieves this using view-dependent propositions.Fig. 4 shows selected iRC11 proof rules.
View-dependent propositions.The propositions of iRC11 are defined as view-monotone predicates vProp ≜ View mon − −− → iProp, where iProp is the type of Iris propositions.Monotonicity is required to ensure that a proposition remains valid when a program step increases the thread's current view.By interpreting propositions implicitly with respect to the current view, the iRC11 surface logic is similar to SC separation logic.The usual separation logic connectives (e.g., quantifiers, separating conjunction) are lifted from iProp to vProp.Hoare triples { } {v.} use view-monotone predicates for the precondition and postcondition .(Like Iris, the postcondition has a binder v for the return value, which we omit if the unit value () is returned.)The rules PointsTo-Load and PointsTo-Store for non-atomic accesses are exactly like their counterparts in SC separation logic.They involve the points-to assertion ℓ ↦ → , which asserts unique ownership of location ℓ with value .
Invariants and objective resources.To share resources among threads, Iris provides the invariant connective , which expresses that a proposition holds at all times.Inv-Alloc says that a proposition can be turned into an invariant .(For simplicity, think of Iris's update as an implication.)Inv-Pers says that invariants are persistent [Jung et al. 2018, §2.3], i.e., it does not assert exclusive ownership.Persistent propositions are duplicable ( ⊣⊢ * ) and can therefore be shared among threads.Inv-Acc says a thread can get unique ownership of the resources described by for the duration of a physically atomic step, provided that ownership is given back afterwards.
Invariants in iRC11 are more restrictive than those in Iris.The shared nature of invariants mandates their interpretation to be independent of the view, called objective in iRC11.This restriction is formalized by the premise objective( ) of Inv-Alloc.Crucially, the points-to assertion ℓ ↦ → is  not objective, and thus cannot immediately be shared through invariants.To put a non-objective proposition (such as ℓ ↦ → ) into an invariant, VA-intro is used to freeze with respect to the thread's current view, i.e., to turn into ⊒ * @ for the thread's current view .The viewseen predicate ⊒ asserts that the thread's current view is at least , and is formally defined as ⊒ ≜ cur .(cur ⊒ ).The view-at modality @ says that holds at view , and is formally defined as @ ≜ ′ .( ).Since @ is objective by definition, it can be put in invariants and transferred to other threads.VA-elim allows retrieving the original resource as soon as the thread obtains ⊒ , e.g., by receiving it from another thread via release-acquire synchronization.
To reason about atomic operations, iRC11 provides the atomic points-to assertion ℓ ↦ → at ℎ, which asserts ownership of location ℓ with history ℎ.To constrain relaxed behaviors such as reading stale values, the history-seen observation ℓ ⊒ sn ℎ ′ asserts a thread's observation of writes in the sub-history ℎ ′ .For space reasons, we refer to Dang et al. [2020] for details.

Linearizability and Logically Atomic Triples in RMC
In an SC memory model, an object is linearizable if (1) each of its operations appear to take effect instantaneously at some point during the operation's execution, called the linearization point; and (2) the execution order of operations at their linearization points, called the linearization order, adheres to the sequential specification of the object.In separation logics such as Iris, linearizability is commonly formulated using logically atomic triples (LAT) [Birkedal et al. 2021;da Rocha Pinto et al. 2014].These triples are of the form ⟨ì .( ì )⟩ ⟨ . (ì , )⟩, and express that has an atomic instruction, called the commit point, which takes the precondition ( ì ) and transforms it into the postcondition ( ì , ), where is the return value.The LAT of the push operation of an SC stack is ⟨vs.Stack( , vs)⟩ push( , ) ⟨Stack( , :: vs)⟩, and expresses that a push of to the stack located at takes effect instantaneously at the linearization point, causing the abstract state to be atomically changed from vs to :: vs. Since LATs allow for interference from other threads, such as concurrent calls to push and pop, the exact value of vs need not be known when the function is called.As such, the pre-and postcondition of a LAT can be bound by (a number of) quantifiers ì (here, the single quantifier vs).The fact that a LAT ensures that an operation behaves as if it were atomic is witnessed by LAInv-Acc in Fig. 4, which says that invariants can be accessed around a LAT.
Out-of-order executions and event reordering.A naive adaptation of linearizability to RMC does not work due to out-of-order executions.Consider an RMC version of Treiber [1986]'s stack implemented by Dang et al. [2022] in Fig. 5. 2 The stack is represented as a linked list of nodes pointed to by a head pointer.The method push creates a new node, and tries to append it to the list by modifying the head pointer.The CAS (atomic compare-and-swap) loop is used to test whether a stale value for the head pointer was read, or the head pointer was changed by a concurrent push or pop, and if so tries again.The method try_pop tries to remove a node from the list and returns its value.If the list is empty, ⊥ (null) is returned.The method try_pop also uses a CAS loop to where : Fig. 6.Linearizable history specification of Treiber's stack.
detect concurrent modifications.Crucially, push uses a release CAS on L7, which makes sure the thread's current view is passed to the corresponding try_pop on L11, similar to the message-passing example in §2.1.Release-acquire synchronization is also necessary to ensure that the value written at L3 is safe to read at L16 (recall, data races on non-atomic accesses have undefined behavior).
To express linearizability, we consider the events Init, Push( ), Pop( ), and EmptyPop.The commit points of Push and Pop are successful CAS operations on L7 and L15, respectively, which atomically change the linked list.The commit point of EmptyPop is on L11 in case ⊥ is read.It is important to point out that the acquire read on L11 can read a stale value, which is the main cause of complication compared to SC.Consider the example from Fig. 1, where a thread successfully performs push(s,1) on an initial stack, and another thread subsequently performs try_pop(s).Now may fail to read the latest value written by at L11 and commit an EmptyPop event.But the execution Push(1); EmptyPop contradicts the stack's sequential specification.Dang et al. address this problem through linearizable history specifications.Their key idea is to allow the linearization order to diverge from the execution order, provided causal consistency is preserved.In the example above, this means the EmptyPop event of is allowed to be linearized before the Push(1) event of .Fig. 6 gives the specification of Treiber's stack. 3Compared to the SC version, the Stack( , ) predicate no longer involves a list of values representing the abstract state.Instead, it involves a collection of events, where each event ( ) is uniquely identified by an id ( ).An event consists of (1) type: Init, Push( ), Pop( ), EmptyPop; (2) sync: the sync-view to express synchronization; (3) eview: the event-view collection that tracks which events happened before.In the example above, the sync-view is displayed as orange dots in Fig. 1.The predicate SeenStack( , ) tracks the collection of event ids that have been observed by the thread.
The Stack-Push-Spec rule says that the operation adds a new event ( with fresh id ), increases the current view (from 0 to ), and increases the thread's observation (from 0 to ).The Stack-Try-Pop-Spec rule is similar with the exception of the highlighted area .Since the collection is a priori unordered, Stack-Linearizable says that it can be turned into a linearization order, represented as a sequence of 's event ids, where: (1) The interpretation predicate interp( , , [], ) asserts that the linearization order satisfies the sequential specification.That is, the events in , ordered by , transform the stack from its initial state [] to some final state .Here, is fully determined from and .
(2) The local happens before predicate lhb( , ) asserts that the linearization order satisfies causal consistency, i.e., whenever the event ′ happens before , the event ′ precedes in .
The interp predicate uses an abstract state transition relation , − − → ′ , which says that event with id transforms state into ′ .A state is not just the list of the values in the stack.The elements ( , , , ) also track the event id of the Push( ) event, the sync-view , and the observation of the thread that performed the push.Let us explain why this bookkeeping is needed.
Selective synchronization.Linearizability in RMC is complicated further by the fact that not all events synchronize with each other.In Treiber's stack, only matching Push and Pop events are synchronized-i.e., on a successful pop, only the sync-view of the thread that pushed the value is transferred.The blue parts in Fig. 6 ensure that the sync-view and observation of the thread that performed a push is transferred to the thread that performs the matching pop.Dang et al. [2022] show that linearizable history specifications are applicable to a variety of concurrent objects.The highlighted parts in Fig. 6 are specific to Treiber's stack, while the other parts are essentially generic.Despite the reuse of the specifications, Dang et al. did not consider reuse of proofs.For each concurrent object, they needed to build a new proof from scratch-which involves defining representation predicates such as Stack and SeenStack using ghost state, and proving that they satisfy rules such as those in Fig. 6.In this paper, we observe that a large portion of the proof follows a common structure that can be captured in a reusable library.

PROOF STRUCTURE RECIPE
Our library consists of a number of separation logic predicates and proof rules to keep track of the object modification order (OMO), which generalizes the modification order in the C11 memory model.Instead of being global to the whole memory, OMO keeps track of the coherence order of just the concurrent object in question, and therefore allows object-local reasoning.We show that to extend the OMO with a new event, it is not necessary to consider an arbitrary permutation.New events can simply be inserted into the current order, allowing us to reuse the properties of the existing OMO to prove the desired properties of the updated OMO.We present the predicates of our OMO library ( §3.1), followed by the proof rules to insert events.Our proof rules are optimized to minimize proof obligations for the case of inserting a totally-ordered write (TOW) event at the end ( §3.2), a read-only event in the middle ( §3.3), or an arbitrary event in the middle ( §3.4).Fig. 7 contains an overview of the OMO proof rules; the parts in green will be explained in §4.
The predicates of our library are defined on top of Iris's primitive constructs for ghost state.They should be considered abstract-implementation details are hidden to the user through our proof rules.Implementation details can be found in our Coq mechanization [Park et al. 2024].

Head pointer OMO
Physical structure Fig. 8. Treiber's stack representation predicates (below) with an illustration of the key invariants (above).Some Load events that are not used as commit points of Treiber's stack are omi ed in head pointer's OMO structure for simplicity.The parts in orange correspond to operations discussed in §3.2 and §3.3.
The parameter is a list of lists, rather than a list, to distinguish write and read-only events.Given = [ì 1 , . . ., ì ] and Σ = [ 1 , . . ., ], the head of each list of events ì represents a write transition from −1 to , while the elements in the tail of ì represent a read-only transition from to itself.In §3.3 we show how distinguishing read-only events enables simpler proof rules.We call the th element of and Σ the th generation.The predicate interp omo ( , , , Σ) says that and Σ represent a valid chain of transitions starting in with events , and lhb omo ( , ) says that ensures causal consistency.The rules OmoAuth-Linearizable and Omo-Compatible collectively say that OMO correctly encodes the sequential specification and causal consistency.
The snapshot predicate OmoSnap( , , , ) is a persistent snapshot of the intermediate abstract state after interpreting the events in the linearization order up to .OmoSnap-Get produces snapshots, while OmoAuth-OmoSnap says that and match up with the Σ parameter of OmoAuth.
The rule OmoAuth-Alloc initializes the predicate OmoAuth from an initial event ( , ).The initial event is essential to support initialization parameters such as data structure size.
The seen event predicate ⊒ provides persistent knowledge of thread-local observation of events in the set .When OmoAuth is initialized (OmoAuth-Alloc) or modified by inserting a new event (OmoAuth-Insert-Last, OmoAuth-Insert-Ro, OmoAuth-Insert), the new event is observed at the sync-view of .Observations can be merged and split, i.e., ⊒ Example.Fig. 8 defines the predicates Stack( , , , ) and SeenStack( , , ) for Treiber's stack.They have more parameters ( and ) compared to Fig. 6, which are used for library abstraction ( §4.1).For now you can assume these parameters are existentially quantified.
The predicate Stack( , , , ) is decomposed into (1) the OmoAuth predicate; (2) the ownership of the shared head pointer ↦ − − → ( , ); and (3) data structure specific invariants.Instead of using iRC11's primitive atomic points-to assertion ↦ → at ℎ for the head pointer, we use ↦ − − → ( , ).We discuss this predicate in detail in §4.1, but for now it is sufficient to know that it equips the location with a set of event ids for Load, Store and Update (CAS) and an OMO structure .The predicate SeenStack( , , ) is decomposed into (1) the seen event predicate for general OMO (⊒ ) to track observations to the stack; (2) the seen event predicate for OMO location (SeenLoc( , , )) to track observations to the head pointer ( §4.1); and (3) data structure specific invariants.The data structure specific invariants for Treiber's stack are as follows.
Stk1 Every event to the head pointer ( ) has a corresponding event for Treiber's stack ( ).This relation is established whenever is committed at the instruction point of , and is visualized by the green dotted lines in Fig. 8.This is formalized by , − −−− → , whose exact meaning is discussed in §4.2.The intermediate abstract state ( ) for the event ( ) matches up with the corresponding value of the head pointer ( ) and the physical linked list structure, formalized by PhysList( , ), The PhysList predicate is similar to the usual representation predicate for linked-lists in separation logic.The main difference is that we use the persistent points-to assertion ↦ →□ , which compared to ↦ → is freely duplicable but forbids any operation other than load [Vindum and Birkedal 2021] (nodes of Treiber's stack are immutable).Stk2 The green dotted lines are monotone.This is formalized by CWMono( , ), see §4.2.Stk3 An observation of an event for Treiber's stack ( ) is equivalent to the observation of the corresponding memory event ( ).This invariant is marked in red in Fig. 8.
Representation predicates for other concurrent data structures are defined with a similar pattern: an OmoAuth predicate to govern the insertion of events, predicates to describe the state of concurrent component objects (like ↦ − − → ( )), and data structure specific invariants.The key here is that OmoAuth directly captures the linearizability condition for a given data structure.

Proof Rule for Totally Ordered Write Events
The rule OmoAuth-Insert-Last allows for the insertion of a totally-ordered write (TOW) event into the OMO structure.Compared to the rule for general write events ( §3.4) the premises are easy to prove.Since there is no reordering, we do not have to re-establish interp omo .We only need to prove that there is a transition , − − → ′ from the last abstract state with the new event to the new state ′ .This is the minimal condition to re-establish the correctness of the linearization order.The rule transforms OmoAuth by adding { ↦ → } to , and appending [ ] and ′ to end of and Σ, respectively.The rule requires an observation of the event view (⊒ ) with .eview= , and returns an observation that includes the new event (⊒ ({ } ∪ )).
Example.TOW events are very common in libraries for RMC, supported by the fact that all write events in the concurrent objects we verified are totally ordered, with a few exceptions such as the Dequeue event in the Michael-Scott queue ( §6.2).As an example, consider the situation in Treiber's stack in Fig. 8, where we commit a Pop(3) event (marked orange) after a successful CAS (L15 in Fig. 5).By OmoAuth-Insert-Last, we only need to prove a one-step state transition.We can deduce that the last abstract state is [3,2].The return value 3 matches with the first element of the abstract state by the invariant Stk1 in the previous subsection.Thus, by setting the new abstract state to be [2], we can prove a one-step state transition which immediately re-establishes the invariant.

Proof Rule for Read-Only Events
The rule OmoAuth-Insert-Ro allows for the insertion of a read-only event into the middle of the OMO structure.We have a special treatment for read-only events since they do not change the abstract state after taking a state transition, which allows most of the conditions on the linearization order to be reused when re-establishing it.As a result, the rule only requires one to prove (1) a one-step state transition that does not change the abstract state ( , − − → ), even if the new event is inserted in the middle; and (2) causal consistency for just the new event (marked in red).These are the minimal conditions to re-establish the correctness of the linearization order.Example.As EmptyPop of Treiber's stack is a read-only event, OmoAuth-Insert-Ro can be used in this case.Consider the situation in Fig. 8 where we insert an EmptyPop event at the 2nd generation (marked orange) after reading ⊥ at timestamp 2 from the head pointer (L11 in Fig. 5).Among the two obligations, the one-step state transition can be easily proven by the invariant Stk1 since reading ⊥ ensures that the intermediate abstract state of the 2nd generation is empty.The second condition on causal consistency can be proven by Stk2 and Stk3.If the thread has observed a later event (e.g., Push(2)), it would have also observed the corresponding message (e.g., at 3 ), contradicting the fact that it read ⊥ at 2 from the head pointer.Proving only these conditions is sufficient to re-establish the invariant on the linearization order after insertion.

Proof Rule for General Write Events
The rule OmoAuth-Insert covers the general case of inserting a write event in the middle of the OMO structure.This rule is used in the verification of the Michael-Scott queue ( §6.2), where a Dequeue event requires the linearization order to be reordered.Due to its generality, this rule does not preserve the correctness of the previous linearization order.A new write event can invalidate the interpretability of later events with respect to the sequential specification.Hence one needs to re-establish the chain of state transitions for all later events captured by the interp omo premise.However, causal consistency only needs to be proven for the new event (marked in red).
The invalidation of later events may invalidate snapshots taken using the predicate OmoSnap.To cope with this problem without losing persistence of the predicate, we equip OmoAuth and OmoSnap with a parameter representing the version id.When using OmoAuth-Insert, one gets a new version id ′ , thereby invalidating snapshots with the old version id .

PROOF COMPOSITION RECIPE
Concurrent objects are often built on top of another.For example, Folly's MPMC queue [Meta 2023] is built on top of an SPSC queue, which in turn is built on top of a turn sequencer (see §6.3).To make verification scalable and compositional, it is crucial to observe library abstraction, i.e., the proof of the upper-level object is done purely in terms of the specification of the lower-level object, without inspecting its implementation.To this end, we observe that the linearization point of an upper-level object event often coincides with that of its lower-level object event.We capture these relations using the commit-with relation of our OMO library, which is inspired by the well-known simulation techniques used in proving behavior refinement [Brookes and Rounds 1983].
We first generalize linearizable history specifications to composable linearizability specifications, which expose the necessary details on the events of the lower-level object to enable compositional verification of upper-level objects ( §4.1).We make shared locations the lowest-level objects with corresponding composable linearizability specifications by equipping them with an OMO structure.Finally, we discuss the formal definition of the commit-with relation in our OMO library through the persistent separation logic predicate , ℓ − −−− → ℓ and the exclusive permission Token( , ) ( §4.2).These predicates formalize the green dotted lines in the verification of Treiber's stack in Fig. 8.

Composable Linearizability Specification
The rule Stack-Try-Pop-Spec-Comp in Fig. 9 gives the composable linearizability specification for the pop operation of Treiber's stack.The differences compared to Stack-Try-Pop-Spec from §2.3 are highlighted : (1) the postcondition contains a permission Token( , ), which is needed to establish a commit-with relation (more details in §4.2); and (2) the Stack predicate exposes and includes postconditions (( , ) ⇝ ′ and ( , ) ⇝ ′ ) to guarantee that the OMO structure is constructed incrementally.To see how these conditions are used in compositional proofs, let us first describe the lower-level object of Treiber's stack-a shared location for the head pointer-in Location history OMO structure Fig. 10.Sample evolution of the history and OMO structure on a shared location.(For brevity, we omit the memory ordering and the previous value in Update events.) Fig. 11.Commi ing an upper-level object event ℎ4 at the commit point of a lower-level object event ℓ4 .
The key to equipping shared locations with an OMO structure is their sequential specification ( , , 0 , −,− − −− →).The abstract states of shared location are of the form ( , , ), where is the event id of the latest message, is its value, and is its released view.The event types are Init( , ) for initialization, Load( , , ) for reading , Store( , , ) for writing , and Update( , , , , ) for atomically updating (e.g., through a CAS) the location from value to new value .Event types are equipped with the acquired view and memory ordering .The abstract state transition relation , − − → ′ gives the semantics of memory accesses.The most interesting case is the one for Update.A transition ( , , ) ′ , − −− → ( ′ , ′ , ′ ) requires the previous abstract state's event id to coincide with that of the one specified in the event = ⟨type : Update( , , , , ), • • •⟩.This is needed to forbid future write events from being inserted between and ′ .

Compositional Linearizability Verification
To enable compositional proofs, our OMO library makes it possible to connect the linearization point of upper-level object event to that of a lower-level object event ℓ .These relations are formalized using the commit-with predicate , ℓ − −−− → ℓ , which provides persistent knowledge of a commit-with relation between and ℓ in OMOs with logical ids and ℓ , respectively.
The OMO allocation rule (OmoAuth-Alloc) and insertion rules (e.g., OmoAuth-Insert) illustrate that the , ℓ − −−− → ℓ predicate is created when is inserted into .In doing so, the rule consumes the exclusive permission Token( ℓ , ℓ ) for the lower-level event, and provides the exclusive permission Token( , ) for the higher-level event.The initial permission Token( ℓ , ℓ ) is produced by the composable linearizability specifications of the memory operations (e.g., OmoLoc-Load).The rule Stack-Try-Pop-Spec-Comp exposes the token Token( , ) so that one can verify higher-level objects that use Treiber's stack as the lower-level object.
The transfer of tokens is visualized in Fig. 11.At the common linearization point for the upperlevel object ℎ and lower-level object ℓ , inserting the event ℓ4 to ℓ produces Token( ℓ , ℓ4 ), which is subsequently consumed to insert the event ℎ4 to ℎ , producing Token( ℎ , ℎ4 ) that can also be consumed by a further upper-level object atomically.
To link lower-and higher-level objects, it is often useful to ensure that their linearization orders are monotone.As demonstrated in §3.3, monotonicity of the stack invariant ( Stk2) is used to re-establish causal consistency.The commit-with monotonicity predicate CWMono( , ) provides the persistent knowledge (through Iris's persistence modality □) that the events in the OMO with logical id are monotone w.r.t.its lower-level object.It is defined using Omo ≤ ( , 1 , 2 ), which persistently tracks the order of the generations of events 1 and 2 .Formally, CWMono says that any generation ordering of two upper-level events, Omo ≤ ( , , ′ ), follows from that of the events projected to the same lower-level object ℓ , Omo ≤ ( ℓ , ℓ , ′ ℓ ).Example.In the definition of the Stack( , , , ) predicate (Fig. 8), the OMO points-to assertion ↦ − − → ( ) is used for the head pointer, using it as a lower-level object.The green dotted lines are formalized using the commit-with predicate , − −−− → .In the proofs of the LATs for the push and pop operations, a new commit-with relation is established by consuming the head pointer's Token obtained by reading/writing to it.Monotonicity of the commit-with relation CWMono( , ) is re-established because in the case of (Push or Pop), new write events of both head pointer and Treiber's stack are appended to the end; in the case of (EmptyPop) a new EmptyPop event is inserted at the same generation with the corresponding load event on the head pointer.

PROOF AUTOMATION RECIPE
As the last piece of our proof recipe, we develop automation for the proof rules from §3 and §4 by building upon the Diaframe [Mulder et al. 2022] proof automation framework for Iris.We briefly review Diaframe ( §5.1) and address the challenges to apply it to RMC: maintaining an up-to-date lower bound on the current thread view ( §5.2), and handling invariants where the physical state of a concurrent object no longer fully determines its logical state ( §5.3).

Background on Diaframe
The backbone of Diaframe is a goal-directed proof search technique-it decides the next step in the proof by looking at the current goal and the available resources in the context.For instance, when the current goal concerns a write on memory location ℓ and if a points-to assertion ℓ ↦ → is in some invariant, then Diaframe opens that invariant and performs the write operation using ℓ ↦ → .After the operation, Diaframe tries to close the invariant to proceed onto the next step.This pattern is repeated until the proof is finished, or when the automation cannot find a way to proceed.
Closing the invariant is the main hurdle here.To see why, suppose we have an idealized invariant ∃ , , .↦ → * ( , ) * ( , ).We can classify resources in invariants into three major categories: (1) physical resources such as ℓ ↦ → that represent physical state; (2) logical resources such as ghost resources ( ( , ) in our example); (3) connective resources such as ( , ) that relate physical state (e.g., the stored value ) to logical state (e.g., the value ).A priori, the values and are not directly related to the physical value .However, to uphold the invariant after writing a new value to location ℓ, we must find appropriate ′ and ′ so that we can derive ( , ′ ) and ( ′ , ′ ) from the resources ( , ) and ( , ).The specification of the write operation itself does not do this for us-the corresponding update rules for logical resources must be determined separately.Note that a wrong choice of existential variables , or will make the goal unprovable.
Diaframe tackles this by restoring invariants from left to right, and having an extensible hint database of update rules.For example, if we register a hint ( , ) ⊢ ( , ) in Diaframe (where can be determined from , and ) the invariant gets restored as follows in the proof: The order of resources in the invariant is crucial for this strategy to work.It is generally advised to place physical resources earlier than any other resources in the invariant, so that Diaframe can infer the logical state correctly from the physical state.Additionally, Diaframe needs to be provided with hints for manipulating the connective resources and logical resources .

Exposing Views in View-Dependent Predicates
Release-writes in RMC synchronize the view of the writer to subsequent acquire-readers.This is reflected in the logic as a precondition "∃ .⊒ " to release-write operations-i.e., the release thread provides a lower bound for its current view, which the acquiring thread can then rely upon.It is crucial that this lower bound is up-to-date enough.Proofs will otherwise get stuck, since the acquiring thread cannot provably witness required resources.
Suppose we try to prove ∃ .⊒ * ∃ .(@ ) * ( ⊔ ⊑ ) given * ⊒ in our context.This goal is analogous to the proof obligation for verifying some release-writes: is a lower bound on the view of our current thread, is a lower bound of a view that we want to transfer to another thread by a release-write, and "∃ .@ * ( ⊔ ⊑ )" is the resource put in an invariant to transfer , where the recipient will obtain by using VA-elim after acquire-loading .The hard part of this goal is that does not necessarily hold at view : the lower bound on the view of our current thread may not be up-to-date.This means we cannot just instantiate with : we must use VA-intro on to obtain a for which @ * ⊒ holds, and then instantiate with ⊔ .To address this, we set up Diaframe to always use VA-intro to all view-dependent resources before each access to shared memory.This discipline makes sure we always have an up-to-date lower bound on the view of our thread around every memory access, which in turn ensures that we have sufficient information in the logic to verify release-writes.We will thus be able to prove the goal discussed above: before performing release-write, we directly apply VA-intro to to obtain a for which @ * ⊒ holds, then merge ⊒ with ⊒ to ⊒( ⊔ ).
Example.The proof of push of Treiber's stack crucially relies on up-to-date lower bounds for views.The linearization point of push is a successful release-CAS (L7 in Fig. 5), which synchronizes the writes to the (non-atomic) value and next fields, to the acquire-read in L11 in the pop function.We ensure that this part of the proof goes through automatically: the non-atomic points-to assertions of the value and next fields go through VA-intro, and the joined view is sent to the acquire-reader.

Inferring Logical State
Recall from §5.1 that Diaframe usually infers the logical state from the physical state after symbolically executing instructions.However, the connection between physical and logical states is much less obvious in RMC.Consider the existentially quantified variables in Stack (Fig. 8).Unlike SC, where we only keep the most recent state, in RMC we are required to keep all past states (i.e., inside and Σ) and maintain invariants for all of them.This makes inference of the logical state more difficult, especially in situations where a new event must be inserted before a past state.Even worse, incorrect choices for such variables can cause the proof to reach a stuck state, requiring the proof engineer to manually intervene and backtrack to the point of the incorrect choice.We designed the proof rules in §3 and §4 carefully to reduce the search space of existential variables for Diaframe to explore as follows.First, we constrain the evolution of the linearization order by requiring new events to be inserted only into the current OMO ( §3.1).This constrains the possibilities for choosing a new logical state-it is often enough to choose the correct insertion rule to correctly infer the logical state.In addition, our proof rules require only minimal proof obligations to re-establish the correctness of OMO, decreasing the overhead of closing the invariant.Notably, one does not need to reprove linearizability after every change to the logical state: linearizability always follows from OmoAuth-Linearizable.This is in contrast with partial-order-based specifications [Batty et al. 2013;Raad et al. 2019] that allow more possibilities on the logical state, and have a larger number of proof obligations.
Second, we use the OMO-based specifications of operations on lower-level objects ( §4.1) as a guide for updating the upper-level object.After a write or read-only operation on a lower-level object, a corresponding Token( , ) is added to the proof state.Our hints detect such Tokens, and update the logical state accordingly by choosing the corresponding insertion rule for the composite structure.For example, following the observation that the linearization point of a lower-level object usually corresponds to an event of the same type (i.e., write or read-only) of the upper-level object, the presence of Token ( , ) (indicating that ( ) is a write event), triggers the hint for insertion of a write event (OmoAuth-Insert-Last). 4Similarly, Token ( , ) indicates a read-only event ( ), and triggers the hint for OmoAuth-Insert-Ro.Moreover, we ensure our hints extend the proof state appropriately, e.g., on insertion of a new event ( ) into the OMO, a commit-with relation is added.These can be matched with later proof obligations as demonstrated in the example below.
Third, we ensure that Diaframe stops at the key moments in the proofs where manual reasoning is necessary.For instance, inferring the correct sync-view for Folly's MPMC queue is extremely non-trivial since it is dependent on synchronization done after the linearization point.To these existentially quantified variables, we add a marker-either in the invariant or in the postcondition of the specification.This marker tells Diaframe to stop the proof automation, and explicitly ask for a witness, whenever it would otherwise try to infer a value.Another point of interest is possible linearization points.Due to the complex nature of the OMO predicates, it is difficult for Diaframe to automatically recognize the linearization point.Thus, we make Diaframe stop before attempting to close the invariant, and ask the user whether the linearization point happens now.This approach enables a fruitful collaboration between interactive proofs and proof automation.
Example.We illustrate how these ideas work together during the verification of the Treiber stack's pop.Specifically, we consider the insertion of an EmptyPop event, which happens directly after reading ⊥ in L11 (Fig. 5), producing a location event .Treiber's stack invariant is in Fig. 8.
(1) Before attempting to restore the stack invariant (i.e., Stack( , , . ..)),Diaframe stops and asks whether the linearization point happens now or later.In the case of EmptyPop, linearization happens now, and so the prover tells Diaframe to commit to the linearization point.
(2) The specification Stack-Try-Pop-Spec-Comp dictates the insertion of the EmptyPop event.Specifically, it prescribes a transition from Stack( , , . ..) to Stack( , ⊎ { ↦ → . ..}, . ..) for a fresh event id ∉ dom( ).In the proof, this means we must make OmoAuth do a corresponding transition, and show the Stack predicate holds for these arguments.
This hint is triggered by the Token resource, obtained after symbolically executing the load instruction with OmoLoc-Load.The hint additionally creates related resources, including the event mapping (6) Finally, Diaframe uses the CWMono predicate that we just obtained from (3). 4 There is no hint for the general insertion rule OmoAuth-Insert since it is too complicated for automation.Instead, the user can pause automation, manually apply the rule, and resume automation.

CASE STUDIES
Table 1 compares, for several RMC objects, the line counts of (OMO) our manual proof with OMO; (OMO+Diaframe) our automated proof with OMO and Diaframe; and (Comparison) the existing proof in separation logic for RMC.Using the OMO structure reduces the proof effort by 57%, while the use of Diaframe additionally reduces the proof effort by 58% (overall 81% reduction) in comparison to prior work.5All our predicates and rules for OMO are formalized in Coq with 10,243 SLOC, while Diaframe's hint databases are also formalized in Coq with 4,404 SLOC.

Spinlock
We verify the linearizability of a spinlock with the following operations: (1) new_lock() allocates a spinlock, producing an Init event; (2) try_lock(ℓ) tries to acquire the lock at ℓ, producing either a Lock or a LockFail event; and (3) unlock(ℓ) releases the lock, producing an Unlock event.Fig. 12 illustrates that the commit-with relation between a spinlock and its location's events is bijective (like for Treiber's stack in Fig. 8).The linearizability specification we verify is stronger than the version of Dang et al. [2020]: their specification of try_lock does not rule out spurious failures in a single thread.Safety of the following can only be proved with our specification: ℓ ← new_lock(); assert(try_lock(ℓ)) /* must succeed */; unlock(ℓ).

Michael-Sco eue
We verify the linearizability of the Michael-Scott queue (MSQ) [Michael and Scott 1996] with the following operations: (1) new_queue() allocates a queue, producing an Init event; (2) enq( , ) enqueues a value to the queue at , producing an Enq event; and (3) try_deq( ) tries to dequeue from the queue at , producing a Deq or an EmptyDeq event.The only existing verification of MSQ in RMC separation logic is done for the partial-order-based specification [Dang et al. 2022].
Fig. 12 illustrates the commit-with relation from MSQ.Unlike Treiber's stack, multiple lower-level objects (including the head and next field of each node) participate in the commit-with relation.Remember that write events on different locations may be reordered, preventing these events from having a total order.For example, consider a situation where a thread sequentially performs two try_deq( ) operations without observing an Enq 2 event, producing Deq 1 and EmptyDeq as presented in Fig. 12.In this case, we should not add Deq 1 to the end of OMO structure, otherwise we cannot incrementally construct OMO structure when inserting an EmptyDeq event.In general, to account for possible EmptyDeq events, Deq events are inserted in the middle by OmoAuth-Insert.

Folly's MPMC eue
We verify the linearizability of the MPMC queue and its components from the Folly library [Meta 2023].This case study demonstrates that (1) our proof composition recipe ( §4) works even for a nested composition from location to "turn sequencer", to SPSC queue, and finally to MPMC queue; and (2) our proof structure recipe ( §3) supports external linearization points that are determined by external factors like other threads.The Deq operation has an external linearization point, which is delayed until the matching Enq event is committed.Our recipe supports helping by sending the component Token and an atomic update (representing the obligation to find a linearization point) to another thread, which then consumes it and establishes the commit-with relation.

Elimination Stack
We verify the linearizability of the elimination stack [Hendler et al. 2004] as a composition of the Treiber's stack and exchanger.This case study also demonstrates the effectiveness of our proof composition recipe ( §4) and support of external linearization points.The elimination stack has the same specification as Treiber's stack, but its events can be actually reordered unlike Treiber's stack.

Atomic Reference Counting
We verify the linearizability of a simplified version of Rust's atomic reference counting (Arc) library verified by Dang et al. [2020].This case study showcases the scalability of our proof recipe to complex libraries with (1) a variety of functionalities, totaling 14 specifications for 11 functions; (2) complex synchronization patterns, e.g., collecting distributed ownerships along with thread views to destroy the object in the end; and (3) release and acquire fences.As in the case of the spinlock, our specification is stronger than that of Dang et al. in that ours can rule out spurious failures in most realistic cases while the latter cannot.For example, safety of the following example (simplified for presentation purpose; see [Park et al. 2024]) can only be proved with our specification: ℓ ← new_arc(); assert(drop_arc(ℓ)); /* must succeed in deallocation */.

Clients
To ensure that our specification is strong enough, we verify several programs such as the Treiber's stack client shown in Fig. 1, the spinlock client presented in §6.1, and the Arc client presented in §6.5.Recall from §1, §6.1 and §6.5 that these programs are not provable with weaker (but simpler) specifications that exhibit spurious behaviors.

RELATED WORK
Verification in relaxed memory separation logic.Dang et al. [2020] develop the iRC11 relaxed memory separation logic to verify the Rust type system and its standard libraries.They consider several concurrent objects, including a spinlock and an Arc.Being concerned with the type safety instead of functional correctness, they use weaker specifications than ours ( §6.1, §6.5).
Building on top of iRC11, Dang et al. [2022] verify Treiber's stack, the Michael-Scott queue, and the elimination stack w.r.t.several specifications encoded through LATs.Our compositional linearizability specifications (Fig. 9) further develop their linearizable history specifications.We improve their verifications of the linearizable history specification of Treiber's stack, and the partial-order-based specifications of the Michael-Scott queue and the elimination stack ( §1).Mével et al. [2020] develop the Cosmo separation logic for the multicore OCaml memory model, in which Mével and Jourdan [2021] verify a concurrent bounded queue specified using an LAT.Their work influenced the aforementioned work by Dang et al. [2022] and thus ours.However, their work is based on OCaml memory model, which is less relaxed than C11 (and thus ORC11).
Verification of concurrent objects in RMC.Several strong correctness conditions for concurrent objects have been verified directly on low-level RMC semantics [Batty et al. 2013;Dongol et al. 2018;Raad et al. 2019;Singh and Lahav 2023;Smith et al. 2020].Compared to this work, most of these works do not consider modular client reasoning or mechanization in a proof assistant.Raad et al. [2019] propose partial-order-based specifications that support more relaxed, nonlinearizable objects such as a weak version of the Herlihy-Wing queue [Herlihy and Wing 1990;Raad et al. 2019].In contrast, our total-order-based specifications are tailored for more tight objects.Batty et al. [2013]; Dongol et al. [2018]; Singh and Lahav [2023] advocate for the use of contextual refinement as the correctness condition for RMC libraries to provide a complete library abstraction for clients.Contextual refinement coincides with linearizability in the SC model [Filipović et al. 2010], but it remains future work to investigate if this is the case in RMC too.
Automated verification.Existing work on automated verifications of RMC libraries does not target strong specifications such as linearizability, while automated verifications of linearizability mostly focus on the SC memory model.
GenMC [Kokologiannakis et al. 2019] is a model checker parameterized over various memory models including RC11.Given a complete program, it enumerates all possible executions and checks such properties as code assertions, data race freedom, and liveness of spin loops.GenMC does not support compositional specifications of libraries as it targets closed programs only.Summers and Müller [2018] encode the RSL [Vafeiadis and Narayan 2013], FSL [Doko and Vafeiadis 2016], and FSL++ [Doko and Vafeiadis 2017] relaxed memory separation logics into the Viper tool for automated deductive verification [Müller et al. 2017].They verify Hoare-style specifications of several libraries including Arc and RWSpinlock, but do not consider stronger specifications such as linearizability.They exploit single-location invariants to automate invariant access when the corresponding location is accessed.However, to verify sophisticated concurrent objects (such as the Michael-Scott queue and Folly's MPMC queue) we rely on Iris/iRC11 invariants that involve multiple locations, which are not supported by these encodings in Viper.
In the context of the SC model, Voila [Wolf et al. 2021] is a proof outline checker for LATs in the TaDA logic [da Rocha Pinto et al. 2014], built on top of the Viper framework.Being a proof outline checker, Voila is not fully automated-annotations need to be added for every key step.This is in contrast with fully-automated tools such as Cave [Vafeiadis 2010] and Poling [Zhu et al. 2015] based on shape analysis.Full automation comes at the cost of compositional verification and limits the range of concurrent objects that can be verified.Plankton [Meyer et al. 2022[Meyer et al. , 2023] ] is a recently developed linearizability checker based on the Flow framework [Krishna et al. 2018] that extends the range of supported objects while retaining automation.Plankton performs temporal interpolation to prove the linearization of concurrent search structures with minimal user annotations.None of the aforementioned tools for the SC model are foundational-neither their soundness (meta theory) nor their implementation has been verified in a proof assistant.Diaframe [Mulder and Krebbers 2023;Mulder et al. 2022] addresses this problem by building a tool with strong automation on top of Iris in Coq.Diaframe is extensible with user-defined hints, and has been used to automatically verify various properties in the SC model, including LATs.Its extensibility has been key for us to extend it with support for the iRC11 logic and linearizability in RMC.To the best of our knowledge, we are the first to design a framework for linearizability in RMC that is amenable to automation.

CONCLUSION AND FUTURE WORK
Our proof recipe addresses a problem of scalability that arises in verifying concurrent objects in RMC by encapsulating complexities of relaxed memory in simple abstractions.Our key observation is threefold: the linearization order of a concurrent object usually evolves like the modification order of a shared location ( §3); for library abstraction, the linearization points of an upper-level object event often coincides with that of its lower-level object event ( §4); and even for RMC, many proof obligations can be discharged with pattern-based automation ( §5).As future work, we will apply our recipe to more diverse and complex objects in more realistic settings as follows.
Hindsight reasoning.We expect our recipe to support hindsight reasoning, even without additional reasoning principles such as prophecy variables [Jung et al. 2020].Hindsight reasoning has the common proof pattern of generating an event early in the time and checking whether the event is valid later in retrospect.This is non-trivial in the SC setting, because only the latest state is maintained in the invariant.We expect this to be easier in our recipe for relaxed memory, as it maintains the entire history of events in the OMO structure of the invariant.
Other RMC memory models.We expect that our recipe can be adapted to other RMC memory models for the following reasons.(1) The core ideas of the OMO structure and the commit-with relation are not specific to iRC11 but generally applicable to linearizability in RMC.Essentially, the OMO structure streamlines the reasoning of event reordering ( §3) and the commit-with relation exposes the minimal information to observe library abstraction ( §4).(2) The C11 memory model (on which ORC11 and iRC11 are based) has arguably the most relaxed semantics among practical RMC memory models.The synchronization mechanisms in the OCaml memory model [Dolan et al. 2018] (using atomic locations) and Java memory model [Bender and Palsberg 2019] (using volatile and VarHandle) can be encoded with various C11 memory access modes.
Other styles of specifications.We expect that a large portion of our development can be readily reused in proving other styles of specifications (e.g., partial-order-based specifications).One can use the OMO structure with a trivial state transition system (i.e., ∀ , ′ , , . , − − → ′ holds).By doing so, one can enjoy all the proof rules ( §3, §4) and existing automation supports ( §5).However, one might need to design a sophisticated invariant with less help from the OMO library, and additional hints for Diaframe that facilitate automatic re-establishment of the invariant.

Fig. 1 .
Fig. 1.An execution trace of a le ( ) and right ( ) thread that use a linearizable Treiber's stack in RMC.Orange dots represent linearization points and purple arrows represent the causal order.

Fig. 3 .
Fig. 3. Message passing with release-acquire synchronization from le thread to right thread .

Fig. 12 .
Fig. 12. OMO structures and commit-with relations of a spinlock (le ) and the Michael-Sco queue (right).

Table 1 .
antitative analysis in SLOC of the Coq proofs (excluding empty lines and comments)."N/A" in Comparison column: previous work does not exist."N/A" in OMO + Diaframe column: we leave it as future work due to performance problems in Coq.