github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/tools/gen-critical-atomics/gen-critical-atomics.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"os"
     7  	"os/exec"
     8  	"strings"
     9  	"text/template"
    10  )
    11  
    12  var tmpl = template.Must(template.New("go").Funcs(template.FuncMap{
    13  	"mul": func(x, y int) int {
    14  		return x * y
    15  	},
    16  	"tuple": func(v ...interface{}) []interface{} {
    17  		return v
    18  	},
    19  	"title": strings.Title,
    20  }).Parse(`//go:build baremetal && !tinygo.wasm
    21  
    22  // Automatically generated file. DO NOT EDIT.
    23  // This file implements standins for non-native atomics using critical sections.
    24  
    25  package runtime
    26  
    27  import (
    28  	_ "unsafe"
    29  	"runtime/interrupt"
    30  )
    31  
    32  // Documentation:
    33  // * https://llvm.org/docs/Atomics.html
    34  // * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
    35  //
    36  // Some atomic operations are emitted inline while others are emitted as libcalls.
    37  // How many are emitted as libcalls depends on the MCU arch and core variant.
    38  
    39  {{- define "load"}}{{$bits := mul . 8 -}}
    40  //export __atomic_load_{{.}}
    41  func __atomic_load_{{.}}(ptr *uint{{$bits}}, ordering uintptr) uint{{$bits}} {
    42  	// The LLVM docs for this say that there is a val argument after the pointer.
    43  	// That is a typo, and the GCC docs omit it.
    44  	mask := interrupt.Disable()
    45  	val := *ptr
    46  	interrupt.Restore(mask)
    47  	return val
    48  }
    49  {{end}}
    50  {{- define "store"}}{{$bits := mul . 8 -}}
    51  //export __atomic_store_{{.}}
    52  func __atomic_store_{{.}}(ptr *uint{{$bits}}, val uint{{$bits}}, ordering uintptr) {
    53  	mask := interrupt.Disable()
    54  	*ptr = val
    55  	interrupt.Restore(mask)
    56  }
    57  {{end}}
    58  {{- define "cas"}}{{$bits := mul . 8 -}}
    59  //go:inline
    60  func doAtomicCAS{{$bits}}(ptr *uint{{$bits}}, expected, desired uint{{$bits}}) uint{{$bits}} {
    61  	mask := interrupt.Disable()
    62  	old := *ptr
    63  	if old == expected {
    64  		*ptr = desired
    65  	}
    66  	interrupt.Restore(mask)
    67  	return old
    68  }
    69  
    70  //export __sync_val_compare_and_swap_{{.}}
    71  func __sync_val_compare_and_swap_{{.}}(ptr *uint{{$bits}}, expected, desired uint{{$bits}}) uint{{$bits}} {
    72  	return doAtomicCAS{{$bits}}(ptr, expected, desired)
    73  }
    74  
    75  //export __atomic_compare_exchange_{{.}}
    76  func __atomic_compare_exchange_{{.}}(ptr, expected *uint{{$bits}}, desired uint{{$bits}}, successOrder, failureOrder uintptr) bool {
    77  	exp := *expected
    78  	old := doAtomicCAS{{$bits}}(ptr, exp, desired)
    79  	return old == exp
    80  }
    81  {{end}}
    82  {{- define "swap"}}{{$bits := mul . 8 -}}
    83  //go:inline
    84  func doAtomicSwap{{$bits}}(ptr *uint{{$bits}}, new uint{{$bits}}) uint{{$bits}} {
    85  	mask := interrupt.Disable()
    86  	old := *ptr
    87  	*ptr = new
    88  	interrupt.Restore(mask)
    89  	return old
    90  }
    91  
    92  //export __sync_lock_test_and_set_{{.}}
    93  func __sync_lock_test_and_set_{{.}}(ptr *uint{{$bits}}, new uint{{$bits}}) uint{{$bits}} {
    94  	return doAtomicSwap{{$bits}}(ptr, new)
    95  }
    96  
    97  //export __atomic_exchange_{{.}}
    98  func __atomic_exchange_{{.}}(ptr *uint{{$bits}}, new uint{{$bits}}, ordering uintptr) uint{{$bits}} {
    99  	return doAtomicSwap{{$bits}}(ptr, new)
   100  }
   101  {{end}}
   102  {{- define "rmw"}}
   103  	{{- $opname := index . 0}}
   104  	{{- $bytes := index . 1}}{{$bits := mul $bytes 8}}
   105  	{{- $signed := index . 2}}
   106  	{{- $opdef := index . 3}}
   107  
   108  {{- $type := printf "int%d" $bits}}
   109  {{- if not $signed}}{{$type = printf "u%s" $type}}{{end -}}
   110  {{- $opfn := printf "doAtomic%s%d" (title $opname) $bits}}
   111  
   112  //go:inline
   113  func {{$opfn}}(ptr *{{$type}}, value {{$type}}) (old, new {{$type}}) {
   114  	mask := interrupt.Disable()
   115  	old = *ptr
   116  	{{$opdef}}
   117  	*ptr = new
   118  	interrupt.Restore(mask)
   119  	return old, new
   120  }
   121  
   122  //export __atomic_fetch_{{$opname}}_{{$bytes}}
   123  func __atomic_fetch_{{$opname}}_{{$bytes}}(ptr *{{$type}}, value {{$type}}, ordering uintptr) {{$type}} {
   124  	old, _ := {{$opfn}}(ptr, value)
   125  	return old
   126  }
   127  
   128  //export __sync_fetch_and_{{$opname}}_{{$bytes}}
   129  func __sync_fetch_and_{{$opname}}_{{$bytes}}(ptr *{{$type}}, value {{$type}}) {{$type}} {
   130  	old, _ := {{$opfn}}(ptr, value)
   131  	return old
   132  }
   133  
   134  //export __atomic_{{$opname}}_fetch_{{$bytes}}
   135  func __atomic_{{$opname}}_fetch_{{$bytes}}(ptr *{{$type}}, value {{$type}}, ordering uintptr) {{$type}} {
   136  	_, new := {{$opfn}}(ptr, value)
   137  	return new
   138  }
   139  {{end}}
   140  {{- define "atomics"}}
   141  // {{mul . 8}}-bit atomics.
   142  
   143  {{/* These atomics are accessible directly from sync/atomic. */ -}}
   144  {{template "load" .}}
   145  {{template "store" .}}
   146  {{template "cas" .}}
   147  {{template "swap" .}}
   148  {{template "rmw" (tuple "add" . false "new = old + value")}}
   149  
   150  {{- end}}
   151  {{template "atomics" 2 -}}
   152  {{template "atomics" 4 -}}
   153  {{template "atomics" 8}}
   154  `))
   155  
   156  func main() {
   157  	var out string
   158  	flag.StringVar(&out, "out", "-", "output path")
   159  	flag.Parse()
   160  	f := os.Stdout
   161  	if out != "-" {
   162  		var err error
   163  		f, err = os.Create(out)
   164  		if err != nil {
   165  			panic(err)
   166  		}
   167  		defer f.Close()
   168  	}
   169  	var buf bytes.Buffer
   170  	err := tmpl.Execute(&buf, nil)
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  	cmd := exec.Command("gofmt")
   175  	cmd.Stdin = &buf
   176  	cmd.Stdout = f
   177  	cmd.Stderr = os.Stderr
   178  	err = cmd.Run()
   179  	if err != nil {
   180  		panic(err)
   181  	}
   182  }