reasoning about the c c weak memory model
play

Reasoning about the C/C++ weak memory model Viktor Vafeiadis Max - PowerPoint PPT Presentation

Reasoning about the C/C++ weak memory model Viktor Vafeiadis Max Planck Institute for Software Systems (MPI-SWS) 17 July 2014 Understanding weak memory consistency Read the architecture/language specs? Too informal, often wrong. Read the


  1. Reasoning about the C/C++ weak memory model Viktor Vafeiadis Max Planck Institute for Software Systems (MPI-SWS) 17 July 2014

  2. Understanding weak memory consistency Read the architecture/language specs? ◮ Too informal, often wrong. Read the formalisations? ◮ Fairly complex. Run benchmarks / Litmus tests? ◮ Observe only subset of behaviours. We need a better methodology. . . Viktor Vafeiadis Reasoning about the C/C++ weak memory model 2/34

  3. The C11 memory model Two types of locations: ordinary and atomic ◮ Races on ordinary accesses ❀ error A spectrum of atomic accesses: ◮ Relaxed ❀ no fence ◮ Consume reads ❀ no fence, but preserve deps ◮ Release writes ❀ no fence (x86); lwsync (PPC) ◮ Acquire reads ❀ no fence (x86); isync (PPC) ◮ Seq. consistent ❀ full memory fence Explicit primitives for fences Viktor Vafeiadis Reasoning about the C/C++ weak memory model 3/34

  4. Relaxed behaviour: store buffering Initially x = y = 0. x . store(1 , rlx ); y . store(1 , rlx ); t 1 = y . load( rlx ); t 2 = x . load( rlx ); This can return t 1 = t 2 = 0. Justification: [ x = y = 0] Behaviour observed W rlx ( x , 1) W rlx ( y , 1) on x86/Power/ARM R rlx ( y , 0) R rlx ( x , 0) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 4/34

  5. Release-acquire synchronization: message passing Initially a = x = 0. a = 5; while ( x . load( acq ) == 0); x . store(1 , release ); print( a ); This will always print 5. Justification: W na ( a , 5) R acq ( x , 1) Release-acquire synchronization W rel ( x , 1) R na ( a , 5) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 5/34

  6. Relaxed accesses don’t synchronize Initially a = x = 0. a = 5; while ( x . load( rlx ) == 0); x . store(1 , rlx ); print( a ); The program is racy ❀ undefined semantics. Justification: W na ( a , 5) R rlx ( x , 1) Relaxed accesses race don’t synchronize W rlx ( x , 1) R na ( a , ?) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 6/34

  7. Dependency cycles Initially x = y = 0. if ( x . load ( rlx ) == 1) if ( y . load ( rlx ) == 1) y . store (1 , rlx ); x . store (1 , rlx ); C11 allows the outcome x = y = 1. Justification: R rlx ( x , 1) R rlx ( y , 1) Relaxed accesses don’t synchronize W rlx ( y , 1) W rlx ( x , 1) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 7/34

  8. Given a memory model definition 1. Check that the model is mathematically sane . ◮ For example, it is monotone. 2. Check that it is not too weak . ◮ Provides useful reasoning principles. 3. Check that it is not too strong . ◮ Can be implemented efficiently. 4. Check that it is actually useful . ◮ Admits the intended program optimisations. Viktor Vafeiadis Reasoning about the C/C++ weak memory model 8/34

  9. How does the C11 definition rate? (1/2) Let’s start with some good news. . . Verified compilation of atomic accesses to x86 and Power/ARM. [Batty et al., POPL’11] [Batty et al., POPL’12] [Sarkar et al., PLDI’12] = ⇒ The C11 model is not too strong . Viktor Vafeiadis Reasoning about the C/C++ weak memory model 9/34

  10. How does the C11 definition rate? (2/2) 1. Check that the model is mathematically sane . ✗ No, it is not monotone. 2. Check that it is not too weak . ✗ No, due to dependency cycles. 3. Check that the model is not too strong . ✓ OK, prior work. 4. Check that it is actually useful . ✗ No, it disallows intended program transformations. Viktor Vafeiadis Reasoning about the C/C++ weak memory model 10/34

  11. Part I. Mathematical sanity ◮ Monotonicity ◮ Prefix closure

  12. Monotonicity “Adding synchronisation should not introduce new behaviours” Examples: ◮ Adding a memory fence ◮ Strengthening the access mode of an operation ◮ Reducing parallelism, C 1 � C 2 ❀ C 1 ; C 2 ◮ Expression evaluation linearisation: x = a + b ; t 1 = a ; t 2 = b ; x = t 1 + t 2 ; ❀ ◮ (Roach motel reorderings) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 12/34

  13. Obstacles to monotonicity 1. The axiom for non-atomic reads rf( b ) = a ∧ (isNA( a ) ∨ isNA( b )) = ⇒ hb( a , b ) (in combination with dependency cycles) 2. The axiom for SC reads Viktor Vafeiadis Reasoning about the C/C++ weak memory model 13/34

  14. Sequentionalisation is invalid if ( x . load( rlx ) == 1) if ( y . load( rlx ) == 1) a = 1; if ( a == 1) x . store(1 , rlx ); y . store(1 , rlx ); [ a = x = y = 0] W na ( a , 1) R rlx ( x , 1) R rlx ( y , 1) R na ( a , 1) W rlx ( x , 1) W rlx ( y , 1) rf( b ) = a ∧ (isNA( a ) ∨ isNA( b )) = ⇒ hb( a , b ) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 14/34

  15. SC read restriction There shall be a single total order S on all seq_cst operations [. . . ] such that each seq_cst operation B that loads a value from an atomic object M observes one of the following values: ◮ the result of the last modification A of M that precedes B in S, if it exists, or ◮ if A exists, the result of some modification of M in the visible sequence of side effects with respect to B that is not seq_cst and that does not happen before A, or ◮ if A does not exist, [. . . ] [N1570, §7.17.3.6] rf( b ) = c ∧ isSC( b ) = ⇒ iscr( c , b ) ∨ ¬ isSC( c ) ∧ ∄ a . hb( c , a ) ∧ iscr( a , b ) where iscr( c , b ) def = scr( c , b ) ∧ ∄ d . scr( c , d ) ∧ scr( d , b ) scr( c , b ) def = iswrite locs( b ) ( c ) ∧ sc( c , b ) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 15/34

  16. Strengthening is invalid s 1 = x . load( rlx ); s 2 = x . load( rlx ); x . store(1 , rlx ); x . store(3 , rlx ); y . store(3 , sc ); s 3 = x . load( rlx ); x . store(2 , sc ); y . store(2 , sc ); r = x . load( sc ); t 1 = y . load( rlx ); y . store(1 , sc ); t 2 = y . load( rlx ); t 3 = y . load( rlx ); r = s 1 = t 1 = 1 ∧ s 2 = t 2 = 2 ∧ s 3 = t 3 = 3 — Disallowed W rlx ( x , 1) W rlx ( x , 3) W sc ( y , 3) sc sc W sc ( x , 2) W sc ( y , 2) R sc ( x , 1) sc sc W sc ( y , 1) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 16/34

  17. Strengthening is invalid s 1 = x . load( rlx ); s 2 = x . load( rlx ); x . store(1 , rlx ); x . store(3 , sc ); y . store(3 , sc ); s 3 = x . load( rlx ); x . store(2 , sc ); y . store(2 , sc ); r = x . load( sc ); t 1 = y . load( rlx ); y . store(1 , sc ); t 2 = y . load( rlx ); t 3 = y . load( rlx ); r = s 1 = t 1 = 1 ∧ s 2 = t 2 = 2 ∧ s 3 = t 3 = 3 — Allowed W rlx ( x , 1) W sc ( x , 3) W sc ( y , 3) sc sc sc sc W sc ( x , 2) W sc ( y , 2) R sc ( x , 1) sc sc W sc ( y , 1) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 16/34

  18. Prefix closure “Removing (hb ∪ rf)-maximal events should preserve consistency” ◮ Maximal events should not affect other events ◮ Does not hold because of release sequences Viktor Vafeiadis Reasoning about the C/C++ weak memory model 17/34

  19. Release sequences too strong (relaxed writes) Initially x = y = 0. a = 1; x . store(1 , release ); while ( x . load( acq ) � = 3); x . store(3 , rlx ); a = 2; This program is not racy. The acquire synchronizes with the release. Viktor Vafeiadis Reasoning about the C/C++ weak memory model 18/34

  20. Release sequences too strong (relaxed writes) Initially x = y = 0. a = 1; x . store(2 , rlx ); ( ∗ ) x . store(1 , release ); while ( x . load( acq ) � = 3); x . store(3 , rlx ); a = 2; But this one is racy according to C11. The acquire no longer synchronizes with the release. Same if (*) is in a different thread. Viktor Vafeiadis Reasoning about the C/C++ weak memory model 18/34

  21. Part II. Not overly weak ◮ High-level reasoning principles

  22. Some basic high-level reasoning principles DRF: Race-free programs have SC semantics ≈ Ownership-based reasoning Coherence: SC for single-variable programs ≈ Non-relational invariants; e.g., x ≥ 0 ∧ y ≥ 0. Cumulativity: Transitive visibility for Rel-Acq ◮ Ownership tranfer possible Viktor Vafeiadis Reasoning about the C/C++ weak memory model 20/34

  23. � � � � Release-acquire synchronization: message passing Initially a = x = 0. a = 5; while ( x . load( acq ) == 0); x . store( release , 1); print( a ); This will always print 5. Justification: W na ( a , 5) R acq ( x , 1) Release-acquire synchronization W rel ( x , 1) R na ( x , 5) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 21/34

  24. Rules for release/acquire accesses Relaxed separation logic [OOPSLA’13] Ownership transfer by rel-acq synchronizations. ◮ Atomic allocation ❀ pick loc. invariant Q . � � � � Q ( v ) x = alloc( v ); W Q ( x ) ∗ R Q ( x ) ◮ Release write ❀ give away permissions. � � � � Q ( v ) ∗ W Q ( x ) x . store( v , rel ); W Q ( x ) ◮ Acquire read ❀ gain permissions. � � � � R Q ( x ) t = x . load( acq ); Q ( t ) ∗ R Q [ t :=emp] ( x ) Viktor Vafeiadis Reasoning about the C/C++ weak memory model 22/34

  25. Release-acquire synchronization: message passing Initially a = x = 0. Let J ( v ) def = v = 0 ∨ & a �→ 5. � � � � & a �→ 0 ∗ W J ( x ) R J ( x ) a = 5; while ( x . load( acq ) == 0); � � � � & a �→ 5 ∗ W J ( x ) & a �→ 5 x . store( release , 1); print( a ); � � � � W J ( x ) & a �→ 5 PL consequences: Ownership transfer works! Viktor Vafeiadis Reasoning about the C/C++ weak memory model 23/34

Recommend


More recommend