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 }