github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/memmodel/tso.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 type TSOVariant int 8 9 // TSOModel models all loads and stores as TSO operations, possibly 10 // with additional barriers. This implements TSO using the abstract 11 // machine model of Sewell, et al., "x86-TSO: A Rigorous and Usable 12 // Programmer’s Model for x86 Multiprocessors", CACM Research 13 // Highlights, 2010. 14 type TSOModel struct { 15 // StoreMFence, if true, adds an MFENCE after store 16 // operations. 17 StoreMFence bool 18 19 // MFenceLoad, if true, add an MFENCE before load operations. 20 MFenceLoad bool 21 } 22 23 func (m TSOModel) String() string { 24 s := "TSO" 25 if m.StoreMFence { 26 s += "+store MFENCE" 27 } 28 if m.MFenceLoad { 29 s += "+MFENCE load" 30 } 31 return s 32 } 33 34 func (m TSOModel) Eval(p *Prog, outcomes *OutcomeSet) { 35 outcomes.Reset(p) 36 (&tsoGlobal{p, outcomes, &m}).rec(tsoState{}) 37 } 38 39 // tsoGlobal stores state that is global to a TSO evaluation. 40 type tsoGlobal struct { 41 p *Prog 42 outcomes *OutcomeSet 43 model *TSOModel 44 } 45 46 // tsoState stores the state of a program at a single point during 47 // execution. 48 type tsoState struct { 49 mem MemState // Global memory state. 50 sb [MaxThreads]struct { // Per-CPU store buffer. 51 // overlay records all stores performed by this CPU. 52 overlay MemState 53 // buf, h, and t are the store buffer FIFO. 54 buf [MaxOps]byte 55 h, t int 56 } 57 pcs [MaxThreads]int 58 outcome Outcome 59 } 60 61 func (g *tsoGlobal) rec(s tsoState) { 62 // Pick an op to execute next. 63 var opres int 64 any := false 65 for tid := range g.p.Threads { 66 op := g.p.Threads[tid].Ops[s.pcs[tid]] 67 if op.Type != OpExit { 68 any = true 69 ns := s 70 sb := &ns.sb[tid] 71 switch op.Type { 72 case OpLoad: 73 if g.model.MFenceLoad { 74 // Flush the store buffer. 75 ns.mem |= sb.overlay 76 sb.h, sb.t = 0, 0 77 } 78 79 // Combining the global memory and the 80 // overlay simulates store buffer 81 // forwarding. 82 _, opres = op.Exec(ns.mem | sb.overlay) 83 ns.outcome |= Outcome(opres) << op.ID 84 case OpStore: 85 // Write to the store buffer. 86 sb.overlay, _ = op.Exec(sb.overlay) 87 sb.buf[sb.t] = op.Var 88 sb.t++ 89 90 if g.model.StoreMFence { 91 // Flush the store buffer. 92 ns.mem |= sb.overlay 93 sb.h, sb.t = 0, 0 94 } 95 } 96 ns.pcs[tid]++ 97 g.rec(ns) 98 } 99 } 100 if !any { 101 // This execution is done. We don't care if there's 102 // stuff in the store buffers. 103 g.outcomes.Add(s.outcome) 104 return 105 } 106 107 // Pick a store buffer to pop. 108 for tid := range g.p.Threads { 109 if s.sb[tid].h < s.sb[tid].t { 110 ns := s 111 sb := &ns.sb[tid] 112 ns.mem |= MemState(1 << sb.buf[sb.h]) 113 sb.h++ 114 g.rec(ns) 115 } 116 } 117 }