rumtime: implement __sync libcalls as critical sections

This change implements __sync atomic polyfill libcalls by disabling interrupts.
This was previously done in a limited capacity on some targets, but this change uses a go:generate to emit all of the calls on all microcontroller targets.
This commit is contained in:
Nia Waldvogel
2021-11-22 18:01:50 -05:00
committed by Ayke
parent 8bd39d2fc2
commit 9fa667ce63
5 changed files with 472 additions and 171 deletions
+1 -67
View File
@@ -1,3 +1,4 @@
//go:build cortexm
// +build cortexm
package runtime
@@ -20,73 +21,6 @@ func getCurrentStackPointer() uintptr {
return uintptr(stacksave())
}
// Documentation:
// * https://llvm.org/docs/Atomics.html
// * https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
//
// In the case of Cortex-M, some atomic operations are emitted inline while
// others are emitted as libcalls. How many are emitted as libcalls depends on
// the MCU core variant (M3 and higher support some 32-bit atomic operations
// while M0 and M0+ do not).
//export __sync_fetch_and_add_4
func __sync_fetch_and_add_4(ptr *uint32, value uint32) uint32 {
mask := arm.DisableInterrupts()
oldValue := *ptr
*ptr = oldValue + value
arm.EnableInterrupts(mask)
return oldValue
}
//export __sync_fetch_and_add_8
func __sync_fetch_and_add_8(ptr *uint64, value uint64) uint64 {
mask := arm.DisableInterrupts()
oldValue := *ptr
*ptr = oldValue + value
arm.EnableInterrupts(mask)
return oldValue
}
//export __sync_lock_test_and_set_4
func __sync_lock_test_and_set_4(ptr *uint32, value uint32) uint32 {
mask := arm.DisableInterrupts()
oldValue := *ptr
*ptr = value
arm.EnableInterrupts(mask)
return oldValue
}
//export __sync_lock_test_and_set_8
func __sync_lock_test_and_set_8(ptr *uint64, value uint64) uint64 {
mask := arm.DisableInterrupts()
oldValue := *ptr
*ptr = value
arm.EnableInterrupts(mask)
return oldValue
}
//export __sync_val_compare_and_swap_4
func __sync_val_compare_and_swap_4(ptr *uint32, expected, desired uint32) uint32 {
mask := arm.DisableInterrupts()
oldValue := *ptr
if oldValue == expected {
*ptr = desired
}
arm.EnableInterrupts(mask)
return oldValue
}
//export __sync_val_compare_and_swap_8
func __sync_val_compare_and_swap_8(ptr *uint64, expected, desired uint64) uint64 {
mask := arm.DisableInterrupts()
oldValue := *ptr
if oldValue == expected {
*ptr = desired
}
arm.EnableInterrupts(mask)
return oldValue
}
// The safest thing to do here would just be to disable interrupts for
// procPin/procUnpin. Note that a global variable is safe in this case, as any
// access to procPinnedMask will happen with interrupts disabled.