//go:build baremetal && !tinygo.wasm
// +build baremetal,!tinygo.wasm

// Automatically generated file. DO NOT EDIT.
// This file implements standins for non-native atomics using critical sections.

package runtime

import (
	"runtime/interrupt"
	_ "unsafe"
)

// Documentation:
// * https://llvm.org/docs/Atomics.html
// * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
//
// Some atomic operations are emitted inline while others are emitted as libcalls.
// How many are emitted as libcalls depends on the MCU arch and core variant.

// 16-bit atomics.

//export __atomic_load_2
func __atomic_load_2(ptr *uint16, ordering uintptr) uint16 {
	// The LLVM docs for this say that there is a val argument after the pointer.
	// That is a typo, and the GCC docs omit it.
	mask := interrupt.Disable()
	val := *ptr
	interrupt.Restore(mask)
	return val
}

//export __atomic_store_2
func __atomic_store_2(ptr *uint16, val uint16, ordering uintptr) {
	mask := interrupt.Disable()
	*ptr = val
	interrupt.Restore(mask)
}

//go:inline
func doAtomicCAS16(ptr *uint16, expected, desired uint16) uint16 {
	mask := interrupt.Disable()
	old := *ptr
	if old == expected {
		*ptr = desired
	}
	interrupt.Restore(mask)
	return old
}

//export __sync_val_compare_and_swap_2
func __sync_val_compare_and_swap_2(ptr *uint16, expected, desired uint16) uint16 {
	return doAtomicCAS16(ptr, expected, desired)
}

//export __atomic_compare_exchange_2
func __atomic_compare_exchange_2(ptr, expected *uint16, desired uint16, successOrder, failureOrder uintptr) bool {
	exp := *expected
	old := doAtomicCAS16(ptr, exp, desired)
	return old == exp
}

//go:inline
func doAtomicSwap16(ptr *uint16, new uint16) uint16 {
	mask := interrupt.Disable()
	old := *ptr
	*ptr = new
	interrupt.Restore(mask)
	return old
}

//export __sync_lock_test_and_set_2
func __sync_lock_test_and_set_2(ptr *uint16, new uint16) uint16 {
	return doAtomicSwap16(ptr, new)
}

//export __atomic_exchange_2
func __atomic_exchange_2(ptr *uint16, new uint16, ordering uintptr) uint16 {
	return doAtomicSwap16(ptr, new)
}

//go:inline
func doAtomicAdd16(ptr *uint16, value uint16) (old, new uint16) {
	mask := interrupt.Disable()
	old = *ptr
	new = old + value
	*ptr = new
	interrupt.Restore(mask)
	return old, new
}

//export __atomic_fetch_add_2
func __atomic_fetch_add_2(ptr *uint16, value uint16, ordering uintptr) uint16 {
	old, _ := doAtomicAdd16(ptr, value)
	return old
}

//export __sync_fetch_and_add_2
func __sync_fetch_and_add_2(ptr *uint16, value uint16) uint16 {
	old, _ := doAtomicAdd16(ptr, value)
	return old
}

//export __atomic_add_fetch_2
func __atomic_add_fetch_2(ptr *uint16, value uint16, ordering uintptr) uint16 {
	_, new := doAtomicAdd16(ptr, value)
	return new
}

// 32-bit atomics.

//export __atomic_load_4
func __atomic_load_4(ptr *uint32, ordering uintptr) uint32 {
	// The LLVM docs for this say that there is a val argument after the pointer.
	// That is a typo, and the GCC docs omit it.
	mask := interrupt.Disable()
	val := *ptr
	interrupt.Restore(mask)
	return val
}

//export __atomic_store_4
func __atomic_store_4(ptr *uint32, val uint32, ordering uintptr) {
	mask := interrupt.Disable()
	*ptr = val
	interrupt.Restore(mask)
}

//go:inline
func doAtomicCAS32(ptr *uint32, expected, desired uint32) uint32 {
	mask := interrupt.Disable()
	old := *ptr
	if old == expected {
		*ptr = desired
	}
	interrupt.Restore(mask)
	return old
}

//export __sync_val_compare_and_swap_4
func __sync_val_compare_and_swap_4(ptr *uint32, expected, desired uint32) uint32 {
	return doAtomicCAS32(ptr, expected, desired)
}

//export __atomic_compare_exchange_4
func __atomic_compare_exchange_4(ptr, expected *uint32, desired uint32, successOrder, failureOrder uintptr) bool {
	exp := *expected
	old := doAtomicCAS32(ptr, exp, desired)
	return old == exp
}

//go:inline
func doAtomicSwap32(ptr *uint32, new uint32) uint32 {
	mask := interrupt.Disable()
	old := *ptr
	*ptr = new
	interrupt.Restore(mask)
	return old
}

//export __sync_lock_test_and_set_4
func __sync_lock_test_and_set_4(ptr *uint32, new uint32) uint32 {
	return doAtomicSwap32(ptr, new)
}

//export __atomic_exchange_4
func __atomic_exchange_4(ptr *uint32, new uint32, ordering uintptr) uint32 {
	return doAtomicSwap32(ptr, new)
}

//go:inline
func doAtomicAdd32(ptr *uint32, value uint32) (old, new uint32) {
	mask := interrupt.Disable()
	old = *ptr
	new = old + value
	*ptr = new
	interrupt.Restore(mask)
	return old, new
}

//export __atomic_fetch_add_4
func __atomic_fetch_add_4(ptr *uint32, value uint32, ordering uintptr) uint32 {
	old, _ := doAtomicAdd32(ptr, value)
	return old
}

//export __sync_fetch_and_add_4
func __sync_fetch_and_add_4(ptr *uint32, value uint32) uint32 {
	old, _ := doAtomicAdd32(ptr, value)
	return old
}

//export __atomic_add_fetch_4
func __atomic_add_fetch_4(ptr *uint32, value uint32, ordering uintptr) uint32 {
	_, new := doAtomicAdd32(ptr, value)
	return new
}

// 64-bit atomics.

//export __atomic_load_8
func __atomic_load_8(ptr *uint64, ordering uintptr) uint64 {
	// The LLVM docs for this say that there is a val argument after the pointer.
	// That is a typo, and the GCC docs omit it.
	mask := interrupt.Disable()
	val := *ptr
	interrupt.Restore(mask)
	return val
}

//export __atomic_store_8
func __atomic_store_8(ptr *uint64, val uint64, ordering uintptr) {
	mask := interrupt.Disable()
	*ptr = val
	interrupt.Restore(mask)
}

//go:inline
func doAtomicCAS64(ptr *uint64, expected, desired uint64) uint64 {
	mask := interrupt.Disable()
	old := *ptr
	if old == expected {
		*ptr = desired
	}
	interrupt.Restore(mask)
	return old
}

//export __sync_val_compare_and_swap_8
func __sync_val_compare_and_swap_8(ptr *uint64, expected, desired uint64) uint64 {
	return doAtomicCAS64(ptr, expected, desired)
}

//export __atomic_compare_exchange_8
func __atomic_compare_exchange_8(ptr, expected *uint64, desired uint64, successOrder, failureOrder uintptr) bool {
	exp := *expected
	old := doAtomicCAS64(ptr, exp, desired)
	return old == exp
}

//go:inline
func doAtomicSwap64(ptr *uint64, new uint64) uint64 {
	mask := interrupt.Disable()
	old := *ptr
	*ptr = new
	interrupt.Restore(mask)
	return old
}

//export __sync_lock_test_and_set_8
func __sync_lock_test_and_set_8(ptr *uint64, new uint64) uint64 {
	return doAtomicSwap64(ptr, new)
}

//export __atomic_exchange_8
func __atomic_exchange_8(ptr *uint64, new uint64, ordering uintptr) uint64 {
	return doAtomicSwap64(ptr, new)
}

//go:inline
func doAtomicAdd64(ptr *uint64, value uint64) (old, new uint64) {
	mask := interrupt.Disable()
	old = *ptr
	new = old + value
	*ptr = new
	interrupt.Restore(mask)
	return old, new
}

//export __atomic_fetch_add_8
func __atomic_fetch_add_8(ptr *uint64, value uint64, ordering uintptr) uint64 {
	old, _ := doAtomicAdd64(ptr, value)
	return old
}

//export __sync_fetch_and_add_8
func __sync_fetch_and_add_8(ptr *uint64, value uint64) uint64 {
	old, _ := doAtomicAdd64(ptr, value)
	return old
}

//export __atomic_add_fetch_8
func __atomic_add_fetch_8(ptr *uint64, value uint64, ordering uintptr) uint64 {
	_, new := doAtomicAdd64(ptr, value)
	return new
}
