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  }