• No results found

Our logic abstracts over the executions of the JOM in two ways. For expressions, assertions take the form of innequalities over the final value of expressions. Deduction relies on the transitive properties of inequality of natural number values and the equalities inherent in basic control structures. For example, a let expression will eventually reduce to the same value as its second child expression. For memory accesses, assertions take the form of the relations of the memory

model and deduction proceeds along the lines of previous reasoning about example programs. In the case of expressions this abstraction represents a significant reduction in proof effort. The expression semantics of our model tracks program order, executes memory accesses out- of-order, and emulates compiler optimizations with speculation, all while maintaining a loose coupling to the history semantics via memory access identifiers. As a consequence, reasoning about expressions directly with the semantics would be tedious, requiring an accounting of the many possible outcomes of the step relation for each expression. Moreover, associating memory access expressions to their identifiers and behaviors in the memory model would be difficult.

Here, we detail the assertion language and the semantics of assertions in our logic. In Sections 4.3 and 4.4 we will state and prove theorems using our assertions and the rules of our logic. 4.2.1 Assertions and Deductive Rules

Our assertion language, depicted in Figure 4.4a, is comprised of a standard first order fragment with quantification for labels and natural numbers, ∀V, A, and three core program assertions: expression locations, e @ L, expression relations, V ◦ V. , and memory relations L −−→ LR . We will consider each in turn and give their logical semantics in the next section.

To reason about a program we need a way to identify specific expressions within the program. Further we want to refer to all “instances” of a particular expression, such as library procedures, so that we can reason about all occurrences of the expression. To that end the logic assumes a labeling discipline where all expressions are located with unique label sequences, L. Consider the example program in Figure 4.4d. There, we can locate the read of y with [y] @ l1. Then, if we

wish to prove properties about the read that are general with respect to some larger program in which it appears we can quanitfy over the label sequence that locates the parent let expression. For example, where the parent let expression is located at L we might have ∀L, [y] @ L, l1 ⇒ P,

where P is some assertion in our assertion language.

Once we have located an expression within a program, we reason about its behavior through the values it produces when it has fully executed. In particular, we can use the same label sequence from the initial state of the program to track the expression’s execution until it results in a natural

Expressions e:= . . . Naturals n:= . . . Labels l := . . . Executions E := E; (P, H) | (P, H) Label Seq. L := L; l | l Val. Variables V := L | n Operators ◦:= = | < Relations R:= co | coi | vo | po | push | rf | vvo Assertions A:= false | A =⇒ A |∀V, A | e@ L | V ◦ V | L. −−→ LR Assumptions Γ:= Γ; A | A (a) Language E  e @ L ⇔ match(E, L, e) E  L .= n ⇔ E = E0; E00∧ E00  n @ L E  n ◦ n ⇔ n ◦ n. E  L ◦ n ⇔. ∃n0, L .= n0 ∧ n ◦ n0 E  L1 ◦ L. 2∃n1, E  L1 ◦ n. 1 ∧ ∃n2, E  L2 ◦ n. 2 ∧ n1◦ n2 E  L1 −−→ LR 2∃i1, accessid(E, L1, i1) ∧ ∃i2, accessid(E, L2, i2) ∧ i1 −−→R E i2 E  A1⇒ A2 ⇔if E  A1then E  A2 E  ∀V, A ⇔ ∀V0, E  [V0/V ]A (b) Semantics Γ A ⇔ ∀E, (∀A0 ∈ Γ, E  A0) ⇒ E  A

(c) Models with Assumptions

let x := (l1:[y]) in (l2:x) (d) Example Program

Figure 4.4: Logical Assertion Language and Semantics

number. In the example program of Figure 4.4d, we can initially locate the local variable x using the label sequence L, l2. Now, assume that the read returns the value 1. Then we know that x will

be replaced with the value 1 by substitution in a later state of the program and we can locate 1 with the same label sequence. We record this fact as the expression equality L, l2 .= 1.

Most often we will use the natural equalities inherent between parent and child expressions in our deductions. For example, any let expression will eventually reduce to the final value of its second expression. So, from the example program where the parent let expression is located at L, we know that L .= L, l2. This appears as the rule let-right in Figure 4.5. Then, if we have the

(if s then l1:e1 else l2:e2)@ L if-alt (L, lcnd .= 1 ∧ L .= L, l1) ∨ (L, lcnd .= 0 ∧ L .= L, l2) (let x := l1:e1in l2:e2)@ L let-right L .= L, l2 let x := l1:e1in l2:e2@ L let-left ∃n, L, l1 .= n x @ L, l2, L0 x ∈FV(e2) let x := l1:e1in l2:e2 @ L let-bind L, l2, L0 .= L, l 1 (a) Expression Rules

[s] @ Lr Lr .= n Lr, lloc .= l reads-rf ∃Lw, Lw −−−rf→ Lr L1 −−−−→ Lpush 2 L3 −−−−→ Lpush 4 vo-pushes L1 −−−vo→ L4 ∨ L3 −−−vo→ L2 [s] := s @ L1 L1 −−−vo→ Lr L2 −−−rf→ Lr L1 , L2 cowr L1 −−−co→ L2 L1 −−−co→ L2 L2 −−−co→ L1 co-cycl false (b) Memory Rules

Figure 4.5: Example Rules

L1 −−−co→ L2 L1 −−−−coi→ L2⇒ P L1 L2

∀L3, L1 −−−co→ L3 ⇒ P L1 L3 ⇒ L3 −−−−coi→ L2 ⇒ P L1 L2

co-ind P L1 L2

know that L .= 1.

The memory relations of the semantics are lifted from the identifiers of the history seman- tics to expressions via label sequences that point to memory accesses. The goal is frequently to establish the relationship between a read and a write expression which enables reasoning about the values of read expressions. For the example program, if we know that the read of y at L, l1is

paired with some write located at Lw, i.e. Lw −−−rf→ L, l1, then we can deduce that Lw .= L, l1.

We can also perform reasoning on the relations themselves using the axioms of the memory model. Recall the cowr rule of the semantics in Section 3.4. In Figure 4.5 we have lifted the identifiers of cowr to labels matching the appropriate expressions. We use these rules to establish cycles in the coherence order, and thereby prove a contradiction, for read-write pairings that we wish to eliminate. By eliminating writes from which reads can draw their values, we constrain the values that the read expressions can return and thereby constrain the values that the read expression can be equal to at the expression level. We call this process write elimination and we will discuss it in more detail in Section 4.3.

During the process of write elimination we will nearly always leverage specified orders in some form or another. For example the rule vo-pushes in Figure 4.5 encodes the disjunction in visibility between the heads and tails of two visibility orders as detailed in Section 3.4.2.

Finally, in Figure 4.6, the rule co-ind encodes structural induction with predicates in our asser- tion language over writes at the label sequences L1and L2. Note that co the relation is transitive.

So, to perform induction, we introduce the coi relation to signify that there are no intervening writes (i.e. coi+ = co). This rule allows proofs to incorporate existing invariant based reasoning

about single memory locations in spite of the partial ordering of writes in the JOM. 4.2.2 Semantics

The semantics of our assertion language, E  A, is defined inductively, see Figure 4.4b, where an execution, E, is a finite sequence of program history pairs related by the semantics of the top level transition relation, (P, H) →∗ (P0, H0). The match function recursively locates expressions within

definition) . We use this to follow the evolution of the expression, located at L, across an execution to a value with E  L ◦ n. With that we can reason about binary relations over labels and natural numbers, E  L ◦ n and E  L1◦ L2. In our proofs of Dekker and RingBuffer we use = and <. The

memory relations, L1−−→ LR 2, are also defined in terms of matching expressions. Recall that each

memory operation is assigned an identifier i and the relations are defined over these identifiers. The actionid predicate ensures that the labeled expressions reach the correct identifier and that they reach the correct value (See Appendix H for the definition). Other standard connectives are defined in terms of ⇒, false, and ∀V, A. Finally, we lift our assertions from executions to collections of assertions as in Figure 4.4c.

The careful reader will have noticed that the equational rule for if-alt has a single matching hypothesis of the form e @ L. Further, the semantics of e @ L only match in the original pro- gram. In practice all of the expression level rules require an additional assumption of equality, i.e. ∃n, L .= n that records the execution of the expression. Since that assumption is identical for every rule we have simply added it to the matching requirement and retained the simpler assertion semantics here for clarity.

4.2.3 Soundness

Where Γ ` A is defined by the rules of our logic, we can now state our soundness theorem. Theorem 6(Soundness). if Γ ` A then Γ  A

The proof proceeds by induction on Γ ` A. Thus, we establish Γ  A from the semantic definitions of the assumptions in each rule of the logic.

A full listing of the rules of our logic appears in Appendix G. Proof sketches for the soundness of a few expression and memory ordering rules are included in in Appendix I. Formal proofs of the expression and memory rules appear in the supplementary material.