From 5f2fabb2b15264467ce77951d257727ad0b658b8 Mon Sep 17 00:00:00 2001 From: Renz Bagaporo Date: Fri, 19 Feb 2021 21:41:26 +0800 Subject: [PATCH 1/8] arch: move stdatomic --- components/newlib/CMakeLists.txt | 1 + components/newlib/newlib.lf | 1 + components/{xtensa => newlib}/stdatomic.c | 87 ++++++++++------------- components/riscv/CMakeLists.txt | 1 - components/riscv/linker.lf | 1 - components/xtensa/CMakeLists.txt | 3 - components/xtensa/linker.lf | 2 - 7 files changed, 38 insertions(+), 58 deletions(-) rename components/{xtensa => newlib}/stdatomic.c (64%) diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 077db45299..ba4c124b9b 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -16,6 +16,7 @@ set(srcs "newlib_init.c" "syscalls.c" "termios.c" + "stdatomic.c" "time.c") set(include_dirs platform_include) diff --git a/components/newlib/newlib.lf b/components/newlib/newlib.lf index 5f1c382f8d..d92c04fb0f 100644 --- a/components/newlib/newlib.lf +++ b/components/newlib/newlib.lf @@ -4,3 +4,4 @@ entries: heap (noflash) abort (noflash) assert (noflash) + stdatomic (noflash) diff --git a/components/xtensa/stdatomic.c b/components/newlib/stdatomic.c similarity index 64% rename from components/xtensa/stdatomic.c rename to components/newlib/stdatomic.c index 4dc425c502..23e34c6951 100644 --- a/components/xtensa/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -16,13 +16,13 @@ #include "sdkconfig.h" #include +#include + +#ifdef __XTENSA__ + #include "xtensa/config/core-isa.h" #include "xtensa/xtruntime.h" -//reserved to measure atomic operation time -#define atomic_benchmark_intr_disable() -#define atomic_benchmark_intr_restore(STATE) - // This allows nested interrupts disabling and restoring via local registers or stack. // They can be called from interrupts too. // WARNING: Only applies to current CPU. @@ -37,14 +37,37 @@ XTOS_RESTORE_JUST_INTLEVEL(state); \ } while (0) -#define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \ -{ \ - unsigned state = _ATOMIC_ENTER_CRITICAL(); \ - type ret = *mem; \ - *mem = val; \ - _ATOMIC_EXIT_CRITICAL(state); \ - return ret; \ -} +#ifndef XCHAL_HAVE_S32C1I +#error "XCHAL_HAVE_S32C1I not defined, include correct header!" +#endif + +#define NO_ATOMICS_SUPPORT (XCHAL_HAVE_S32C1I == 0) +#else // RISCV + +#include "freertos/portmacro.h" + +// This allows nested interrupts disabling and restoring via local registers or stack. +// They can be called from interrupts too. +// WARNING: Only applies to current CPU. +#define _ATOMIC_ENTER_CRITICAL(void) ({ \ + unsigned state = portENTER_CRITICAL_NESTED(); \ + atomic_benchmark_intr_disable(); \ + state; \ +}) + +#define _ATOMIC_EXIT_CRITICAL(state) do { \ + atomic_benchmark_intr_restore(state); \ + portEXIT_CRITICAL_NESTED(state); \ + } while (0) + +#define NO_ATOMICS_SUPPORT 1 // [todo] Get the equivalent XCHAL_HAVE_S32C1I check for RISCV + +#endif + + +//reserved to measure atomic operation time +#define atomic_benchmark_intr_disable() +#define atomic_benchmark_intr_restore(STATE) #define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, int success, int failure) \ { \ @@ -105,47 +128,9 @@ return ret; \ } -#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value, ...) \ -{ \ - return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ -} - -#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ -{ \ - bool ret = false; \ - unsigned state = _ATOMIC_ENTER_CRITICAL(); \ - if (*ptr == oldval) { \ - *ptr = newval; \ - ret = true; \ - } \ - _ATOMIC_EXIT_CRITICAL(state); \ - return ret; \ -} - -#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ -{ \ - unsigned state = _ATOMIC_ENTER_CRITICAL(); \ - type ret = *ptr; \ - if (*ptr == oldval) { \ - *ptr = newval; \ - } \ - _ATOMIC_EXIT_CRITICAL(state); \ - return ret; \ -} - -#ifndef XCHAL_HAVE_S32C1I -#error "XCHAL_HAVE_S32C1I not defined, include correct header!" -#endif - -//this piece of code should only be compiled if the cpu doesn't support atomic compare and swap (s32c1i) -#if XCHAL_HAVE_S32C1I == 0 - #pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" -ATOMIC_EXCHANGE(1, uint8_t) -ATOMIC_EXCHANGE(2, uint16_t) -ATOMIC_EXCHANGE(4, uint32_t) -ATOMIC_EXCHANGE(8, uint64_t) +#if NO_ATOMICS_SUPPORT CMP_EXCHANGE(1, uint8_t) CMP_EXCHANGE(2, uint16_t) diff --git a/components/riscv/CMakeLists.txt b/components/riscv/CMakeLists.txt index 0290ca9937..efc891b19e 100644 --- a/components/riscv/CMakeLists.txt +++ b/components/riscv/CMakeLists.txt @@ -11,7 +11,6 @@ else() "expression_with_stack_riscv_asm.S" "instruction_decode.c" "interrupt.c" - "stdatomic.c" "vectors.S") endif() diff --git a/components/riscv/linker.lf b/components/riscv/linker.lf index 2e5681238c..cfcb303cf0 100644 --- a/components/riscv/linker.lf +++ b/components/riscv/linker.lf @@ -3,4 +3,3 @@ archive: libriscv.a entries: interrupt (noflash_text) vectors (noflash_text) - stdatomic (noflash_text) diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 3fa959a936..1511fb5d5a 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -18,9 +18,6 @@ else() "${target}/trax_init.c" ) - if(IDF_TARGET STREQUAL "esp32s2") - list(APPEND srcs "stdatomic.c") - endif() endif() idf_component_register(SRCS ${srcs} diff --git a/components/xtensa/linker.lf b/components/xtensa/linker.lf index 148116f1a1..5227ce02df 100644 --- a/components/xtensa/linker.lf +++ b/components/xtensa/linker.lf @@ -3,8 +3,6 @@ archive: libxtensa.a entries: eri (noflash_text) xtensa_intr_asm (noflash_text) - if IDF_TARGET_ESP32S2 = y: - stdatomic (noflash) [mapping:xt_hal] archive: libxt_hal.a From 2250da111e97c360cf9cebf24fad010365559cd4 Mon Sep 17 00:00:00 2001 From: Sachin Parekh Date: Thu, 22 Apr 2021 14:43:54 +0530 Subject: [PATCH 2/8] stdatomic: Implemented legacy __sync APIs and __atomic_exchange_n These APIs are used when the architecture doesn't have atomic instruction support Closes https://github.com/espressif/esp-idf/issues/6463 --- components/newlib/stdatomic.c | 48 ++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index 23e34c6951..a58b203ccd 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -60,7 +60,11 @@ portEXIT_CRITICAL_NESTED(state); \ } while (0) -#define NO_ATOMICS_SUPPORT 1 // [todo] Get the equivalent XCHAL_HAVE_S32C1I check for RISCV +#ifndef __riscv_atomic // GCC toolchain will define this pre-processor if "A" extension is supported +#define __riscv_atomic 0 +#endif + +#define NO_ATOMICS_SUPPORT (__riscv_atomic == 0) #endif @@ -69,6 +73,15 @@ #define atomic_benchmark_intr_disable() #define atomic_benchmark_intr_restore(STATE) +#define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *mem; \ + *mem = val; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + #define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, int success, int failure) \ { \ bool ret = false; \ @@ -128,10 +141,43 @@ return ret; \ } +#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value, ...) \ +{ \ + return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ +} + +#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ +{ \ + bool ret = false; \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + if (*ptr == oldval) { \ + *ptr = newval; \ + ret = true; \ + } \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + if (*ptr == oldval) { \ + *ptr = newval; \ + } \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + #pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" #if NO_ATOMICS_SUPPORT +ATOMIC_EXCHANGE(1, uint8_t) +ATOMIC_EXCHANGE(2, uint16_t) +ATOMIC_EXCHANGE(4, uint32_t) +ATOMIC_EXCHANGE(8, uint64_t) + CMP_EXCHANGE(1, uint8_t) CMP_EXCHANGE(2, uint16_t) CMP_EXCHANGE(4, uint32_t) From be520f975a3906809e3db6e4fa7f0497db172097 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 6 May 2021 19:25:32 +0200 Subject: [PATCH 3/8] newlib: stdatomic: fix prototypes, don't disable compiler warnings --- components/newlib/stdatomic.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index a58b203ccd..771bfd030c 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -82,7 +82,7 @@ return ret; \ } -#define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, int success, int failure) \ +#define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, bool weak, int success, int failure) \ { \ bool ret = false; \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ @@ -141,12 +141,12 @@ return ret; \ } -#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value, ...) \ +#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value) \ { \ return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ } -#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ +#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \ { \ bool ret = false; \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ @@ -158,7 +158,7 @@ return ret; \ } -#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ +#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \ { \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ type ret = *ptr; \ @@ -169,8 +169,6 @@ return ret; \ } -#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" - #if NO_ATOMICS_SUPPORT ATOMIC_EXCHANGE(1, uint8_t) From d4f2e03e4aa58a395c7479aa7f3b39eceddccf9f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 6 May 2021 19:26:21 +0200 Subject: [PATCH 4/8] newlib: stdatomic: emulate 64-bit atomics on 32-bit SMP SoCs Closes https://github.com/espressif/esp-idf/issues/3163 --- components/newlib/stdatomic.c | 149 +++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index 771bfd030c..993fcd1ac2 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,61 +17,78 @@ #include "sdkconfig.h" #include #include +#include "soc/soc_caps.h" +#include "freertos/FreeRTOS.h" #ifdef __XTENSA__ - #include "xtensa/config/core-isa.h" -#include "xtensa/xtruntime.h" - -// This allows nested interrupts disabling and restoring via local registers or stack. -// They can be called from interrupts too. -// WARNING: Only applies to current CPU. -#define _ATOMIC_ENTER_CRITICAL(void) ({ \ - unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); \ - atomic_benchmark_intr_disable(); \ - state; \ -}) - -#define _ATOMIC_EXIT_CRITICAL(state) do { \ - atomic_benchmark_intr_restore(state); \ - XTOS_RESTORE_JUST_INTLEVEL(state); \ - } while (0) #ifndef XCHAL_HAVE_S32C1I #error "XCHAL_HAVE_S32C1I not defined, include correct header!" #endif -#define NO_ATOMICS_SUPPORT (XCHAL_HAVE_S32C1I == 0) +#define HAS_ATOMICS_32 (XCHAL_HAVE_S32C1I == 1) +// no 64-bit atomics on Xtensa +#define HAS_ATOMICS_64 0 + #else // RISCV -#include "freertos/portmacro.h" - -// This allows nested interrupts disabling and restoring via local registers or stack. -// They can be called from interrupts too. -// WARNING: Only applies to current CPU. -#define _ATOMIC_ENTER_CRITICAL(void) ({ \ - unsigned state = portENTER_CRITICAL_NESTED(); \ - atomic_benchmark_intr_disable(); \ - state; \ -}) - -#define _ATOMIC_EXIT_CRITICAL(state) do { \ - atomic_benchmark_intr_restore(state); \ - portEXIT_CRITICAL_NESTED(state); \ - } while (0) - -#ifndef __riscv_atomic // GCC toolchain will define this pre-processor if "A" extension is supported +// GCC toolchain will define this pre-processor if "A" extension is supported +#ifndef __riscv_atomic #define __riscv_atomic 0 #endif -#define NO_ATOMICS_SUPPORT (__riscv_atomic == 0) +#define HAS_ATOMICS_32 (__riscv_atomic == 1) +#define HAS_ATOMICS_64 ((__riscv_atomic == 1) && (__riscv_xlen == 64)) +#endif // (__XTENSA__, __riscv) -#endif +#if SOC_CPU_CORES_NUM == 1 +// Single core SoC: atomics can be implemented using portENTER_CRITICAL_NESTED +// and portEXIT_CRITICAL_NESTED, which disable and enable interrupts. +#define _ATOMIC_ENTER_CRITICAL() ({ \ + unsigned state = portENTER_CRITICAL_NESTED(); \ + state; \ +}) -//reserved to measure atomic operation time -#define atomic_benchmark_intr_disable() -#define atomic_benchmark_intr_restore(STATE) +#define _ATOMIC_EXIT_CRITICAL(state) do { \ + portEXIT_CRITICAL_NESTED(state); \ + } while (0) + +#else // SOC_CPU_CORES_NUM + +_Static_assert(HAS_ATOMICS_32, "32-bit atomics should be supported if SOC_CPU_CORES_NUM > 1"); +// Only need to implement 64-bit atomics here. Use a single global portMUX_TYPE spinlock +// to emulate the atomics. +static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED; + +// Return value is not used but kept for compatibility with the single-core version above. +#define _ATOMIC_ENTER_CRITICAL() ({ \ + portENTER_CRITICAL_SAFE(&s_atomic_lock); \ + 0; \ +}) + +#define _ATOMIC_EXIT_CRITICAL(state) do { \ + (void) (state); \ + portEXIT_CRITICAL_SAFE(&s_atomic_lock); \ +} while(0) + +#endif // SOC_CPU_CORES_NUM + +#define ATOMIC_LOAD(n, type) type __atomic_load_ ## n (const type* mem, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *mem; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define ATOMIC_STORE(n, type) void __atomic_store_ ## n (type* mem, type val, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + *mem = val; \ + _ATOMIC_EXIT_CRITICAL(state); \ +} #define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \ { \ @@ -169,76 +186,98 @@ return ret; \ } -#if NO_ATOMICS_SUPPORT +#if !HAS_ATOMICS_32 ATOMIC_EXCHANGE(1, uint8_t) ATOMIC_EXCHANGE(2, uint16_t) ATOMIC_EXCHANGE(4, uint32_t) -ATOMIC_EXCHANGE(8, uint64_t) CMP_EXCHANGE(1, uint8_t) CMP_EXCHANGE(2, uint16_t) CMP_EXCHANGE(4, uint32_t) -CMP_EXCHANGE(8, uint64_t) FETCH_ADD(1, uint8_t) FETCH_ADD(2, uint16_t) FETCH_ADD(4, uint32_t) -FETCH_ADD(8, uint64_t) FETCH_SUB(1, uint8_t) FETCH_SUB(2, uint16_t) FETCH_SUB(4, uint32_t) -FETCH_SUB(8, uint64_t) FETCH_AND(1, uint8_t) FETCH_AND(2, uint16_t) FETCH_AND(4, uint32_t) -FETCH_AND(8, uint64_t) FETCH_OR(1, uint8_t) FETCH_OR(2, uint16_t) FETCH_OR(4, uint32_t) -FETCH_OR(8, uint64_t) FETCH_XOR(1, uint8_t) FETCH_XOR(2, uint16_t) FETCH_XOR(4, uint32_t) -FETCH_XOR(8, uint64_t) SYNC_FETCH_OP(add, 1, uint8_t) SYNC_FETCH_OP(add, 2, uint16_t) SYNC_FETCH_OP(add, 4, uint32_t) -SYNC_FETCH_OP(add, 8, uint64_t) SYNC_FETCH_OP(sub, 1, uint8_t) SYNC_FETCH_OP(sub, 2, uint16_t) SYNC_FETCH_OP(sub, 4, uint32_t) -SYNC_FETCH_OP(sub, 8, uint64_t) SYNC_FETCH_OP(and, 1, uint8_t) SYNC_FETCH_OP(and, 2, uint16_t) SYNC_FETCH_OP(and, 4, uint32_t) -SYNC_FETCH_OP(and, 8, uint64_t) SYNC_FETCH_OP(or, 1, uint8_t) SYNC_FETCH_OP(or, 2, uint16_t) SYNC_FETCH_OP(or, 4, uint32_t) -SYNC_FETCH_OP(or, 8, uint64_t) SYNC_FETCH_OP(xor, 1, uint8_t) SYNC_FETCH_OP(xor, 2, uint16_t) SYNC_FETCH_OP(xor, 4, uint32_t) -SYNC_FETCH_OP(xor, 8, uint64_t) SYNC_BOOL_CMP_EXCHANGE(1, uint8_t) SYNC_BOOL_CMP_EXCHANGE(2, uint16_t) SYNC_BOOL_CMP_EXCHANGE(4, uint32_t) -SYNC_BOOL_CMP_EXCHANGE(8, uint64_t) SYNC_VAL_CMP_EXCHANGE(1, uint8_t) SYNC_VAL_CMP_EXCHANGE(2, uint16_t) SYNC_VAL_CMP_EXCHANGE(4, uint32_t) + +#endif // !HAS_ATOMICS_32 + +#if !HAS_ATOMICS_64 + +ATOMIC_LOAD(8, uint64_t) + +ATOMIC_STORE(8, uint64_t) + +ATOMIC_EXCHANGE(8, uint64_t) + +CMP_EXCHANGE(8, uint64_t) + +FETCH_ADD(8, uint64_t) + +FETCH_SUB(8, uint64_t) + +FETCH_AND(8, uint64_t) + +FETCH_OR(8, uint64_t) + +FETCH_XOR(8, uint64_t) + +SYNC_FETCH_OP(add, 8, uint64_t) + +SYNC_FETCH_OP(sub, 8, uint64_t) + +SYNC_FETCH_OP(and, 8, uint64_t) + +SYNC_FETCH_OP(or, 8, uint64_t) + +SYNC_FETCH_OP(xor, 8, uint64_t) + +SYNC_BOOL_CMP_EXCHANGE(8, uint64_t) + SYNC_VAL_CMP_EXCHANGE(8, uint64_t) -#endif +#endif // !HAS_ATOMICS_64 From de5e0fa3bde81a8c6c0a7fe7e529f1f29abd1962 Mon Sep 17 00:00:00 2001 From: Andrei Safronov Date: Thu, 15 Jul 2021 20:36:40 +0300 Subject: [PATCH 5/8] newlib,clang: implement __sync_* atomics for clang/llvm toolchain. --- components/newlib/stdatomic.c | 39 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index 993fcd1ac2..bbd2d17e80 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -75,6 +75,24 @@ static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED; #endif // SOC_CPU_CORES_NUM +#ifdef __clang__ +// Clang doesn't allow to define "__sync_*" atomics. The workaround is to define function with name "__sync_*_builtin", +// which implements "__sync_*" atomic functionality and use asm directive to set the value of symbol "__sync_*" to the name +// of defined function. + +#define CLANG_ATOMIC_SUFFIX(name_) name_ ## _builtin +#define CLANG_DECLARE_ALIAS(name_) \ +__asm__(".type " # name_ ", @function\n" \ + ".global " #name_ "\n" \ + ".equ " #name_ ", " #name_ "_builtin"); + +#else // __clang__ + +#define CLANG_ATOMIC_SUFFIX(name_) name_ +#define CLANG_DECLARE_ALIAS(name_) + +#endif // __clang__ + #define ATOMIC_LOAD(n, type) type __atomic_load_ ## n (const type* mem, int memorder) \ { \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ @@ -158,12 +176,14 @@ static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED; return ret; \ } -#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value) \ -{ \ - return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ -} -#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \ +#define SYNC_FETCH_OP(op, n, type) type CLANG_ATOMIC_SUFFIX(__sync_fetch_and_ ## op ##_ ## n) (type* ptr, type value) \ +{ \ + return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ +} \ +CLANG_DECLARE_ALIAS( __sync_fetch_and_ ## op ##_ ## n ) + +#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool CLANG_ATOMIC_SUFFIX(__sync_bool_compare_and_swap_ ## n) (type *ptr, type oldval, type newval) \ { \ bool ret = false; \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ @@ -173,9 +193,10 @@ static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED; } \ _ATOMIC_EXIT_CRITICAL(state); \ return ret; \ -} +} \ +CLANG_DECLARE_ALIAS( __sync_bool_compare_and_swap_ ## n ) -#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \ +#define SYNC_VAL_CMP_EXCHANGE(n, type) type CLANG_ATOMIC_SUFFIX(__sync_val_compare_and_swap_ ## n) (type *ptr, type oldval, type newval) \ { \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ type ret = *ptr; \ @@ -184,7 +205,9 @@ static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED; } \ _ATOMIC_EXIT_CRITICAL(state); \ return ret; \ -} +} \ +CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n ) + #if !HAS_ATOMICS_32 From b01c197505b80d09598a14bd567a9a8f418d8619 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Wed, 6 Oct 2021 12:03:09 +0100 Subject: [PATCH 6/8] newlib: provide missing atomic libcalls Provide emulated atomic load & store libcalls for u8, u16 & u32 integer types. This is required when building with Clang as llvm does not lower these operations to native load / stores, where as gcc does. Provide `sync_lock_test_and_set` atomic implementations for all supported integer types. Closes https://github.com/espressif/esp-idf/issues/7591. Closes https://github.com/espressif/esp-idf/issues/7592. --- components/newlib/stdatomic.c | 64 ++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index bbd2d17e80..b54d1b5884 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -1,16 +1,8 @@ -// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ //replacement for gcc built-in functions @@ -208,6 +200,24 @@ CLANG_DECLARE_ALIAS( __sync_bool_compare_and_swap_ ## n ) } \ CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n ) +#define SYNC_LOCK_TEST_AND_SET(n, type) type CLANG_ATOMIC_SUFFIX(__sync_lock_test_and_set_ ## n) (type *ptr, type val, ...) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = val; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} +CLANG_DECLARE_ALIAS( __sync_lock_test_and_set_ ## n ) + +#define SYNC_LOCK_RELEASE(n, type) void CLANG_ATOMIC_SUFFIX(__sync_lock_release_ ## n) (type *ptr, ...) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + *ptr = 0; \ + _ATOMIC_EXIT_CRITICAL(state); \ +} +CLANG_DECLARE_ALIAS( __sync_lock_release_ ## n ) + #if !HAS_ATOMICS_32 @@ -267,6 +277,27 @@ SYNC_VAL_CMP_EXCHANGE(1, uint8_t) SYNC_VAL_CMP_EXCHANGE(2, uint16_t) SYNC_VAL_CMP_EXCHANGE(4, uint32_t) +#ifdef __clang__ + +// LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension +// therfore we provide libcalls here when building with the clang toolchain. LLVM thread: https://reviews.llvm.org/D47553. +ATOMIC_LOAD(1, uint8_t) +ATOMIC_LOAD(2, uint16_t) +ATOMIC_LOAD(4, uint32_t) +ATOMIC_STORE(1, uint8_t) +ATOMIC_STORE(2, uint16_t) +ATOMIC_STORE(4, uint32_t) + +SYNC_LOCK_TEST_AND_SET(1, uint8_t) +SYNC_LOCK_TEST_AND_SET(2, uint16_t) +SYNC_LOCK_TEST_AND_SET(4, uint32_t) + +SYNC_LOCK_RELEASE(1, uint8_t) +SYNC_LOCK_RELEASE(2, uint16_t) +SYNC_LOCK_RELEASE(4, uint32_t) + +#endif + #endif // !HAS_ATOMICS_32 #if !HAS_ATOMICS_64 @@ -303,4 +334,11 @@ SYNC_BOOL_CMP_EXCHANGE(8, uint64_t) SYNC_VAL_CMP_EXCHANGE(8, uint64_t) +#ifdef __clang__ + +SYNC_LOCK_TEST_AND_SET(8, uint64_t) +SYNC_LOCK_RELEASE(8, uint64_t) + +#endif + #endif // !HAS_ATOMICS_64 From 710c1f2faddb6792f3a9269113bf7923969aecef Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Tue, 12 Oct 2021 16:28:49 +0100 Subject: [PATCH 7/8] remove `__clang` ifdef around atomic emulation * CI errors led me to believe these were needed, but as it turns out the load/store intrinsics are required even when idf is built by gcc when linking to a clang based project. * remove ... postfix inside `SYNC_LOCK_TEST_AND_SET` expansion --- components/newlib/stdatomic.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index b54d1b5884..6089efd9ee 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -200,7 +200,7 @@ CLANG_DECLARE_ALIAS( __sync_bool_compare_and_swap_ ## n ) } \ CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n ) -#define SYNC_LOCK_TEST_AND_SET(n, type) type CLANG_ATOMIC_SUFFIX(__sync_lock_test_and_set_ ## n) (type *ptr, type val, ...) \ +#define SYNC_LOCK_TEST_AND_SET(n, type) type CLANG_ATOMIC_SUFFIX(__sync_lock_test_and_set_ ## n) (type *ptr, type val) \ { \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ type ret = *ptr; \ @@ -210,7 +210,7 @@ CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n ) } CLANG_DECLARE_ALIAS( __sync_lock_test_and_set_ ## n ) -#define SYNC_LOCK_RELEASE(n, type) void CLANG_ATOMIC_SUFFIX(__sync_lock_release_ ## n) (type *ptr, ...) \ +#define SYNC_LOCK_RELEASE(n, type) void CLANG_ATOMIC_SUFFIX(__sync_lock_release_ ## n) (type *ptr) \ { \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \ *ptr = 0; \ @@ -277,16 +277,6 @@ SYNC_VAL_CMP_EXCHANGE(1, uint8_t) SYNC_VAL_CMP_EXCHANGE(2, uint16_t) SYNC_VAL_CMP_EXCHANGE(4, uint32_t) -#ifdef __clang__ - -// LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension -// therfore we provide libcalls here when building with the clang toolchain. LLVM thread: https://reviews.llvm.org/D47553. -ATOMIC_LOAD(1, uint8_t) -ATOMIC_LOAD(2, uint16_t) -ATOMIC_LOAD(4, uint32_t) -ATOMIC_STORE(1, uint8_t) -ATOMIC_STORE(2, uint16_t) -ATOMIC_STORE(4, uint32_t) SYNC_LOCK_TEST_AND_SET(1, uint8_t) SYNC_LOCK_TEST_AND_SET(2, uint16_t) @@ -296,16 +286,19 @@ SYNC_LOCK_RELEASE(1, uint8_t) SYNC_LOCK_RELEASE(2, uint16_t) SYNC_LOCK_RELEASE(4, uint32_t) -#endif +// LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension. LLVM thread: https://reviews.llvm.org/D47553. +// Even though GCC does transform them, these libcalls need to be available for the case where a LLVM based project links against IDF. +ATOMIC_LOAD(1, uint8_t) +ATOMIC_LOAD(2, uint16_t) +ATOMIC_LOAD(4, uint32_t) +ATOMIC_STORE(1, uint8_t) +ATOMIC_STORE(2, uint16_t) +ATOMIC_STORE(4, uint32_t) #endif // !HAS_ATOMICS_32 #if !HAS_ATOMICS_64 -ATOMIC_LOAD(8, uint64_t) - -ATOMIC_STORE(8, uint64_t) - ATOMIC_EXCHANGE(8, uint64_t) CMP_EXCHANGE(8, uint64_t) @@ -334,11 +327,12 @@ SYNC_BOOL_CMP_EXCHANGE(8, uint64_t) SYNC_VAL_CMP_EXCHANGE(8, uint64_t) -#ifdef __clang__ - SYNC_LOCK_TEST_AND_SET(8, uint64_t) SYNC_LOCK_RELEASE(8, uint64_t) -#endif +// LLVM has not implemented native atomic load/stores for riscv targets without the Atomic extension. LLVM thread: https://reviews.llvm.org/D47553. +// Even though GCC does transform them, these libcalls need to be available for the case where a LLVM based project links against IDF. +ATOMIC_LOAD(8, uint64_t) +ATOMIC_STORE(8, uint64_t) #endif // !HAS_ATOMICS_64 From 6a57c4d5d52eba48b8bded54895b7becbe2764d7 Mon Sep 17 00:00:00 2001 From: Andrei Safronov Date: Wed, 20 Oct 2021 23:33:53 +0300 Subject: [PATCH 8/8] newlib: fix clang atomics Correction of the SYNC_LOCK_* macros in stdatomic --- components/newlib/stdatomic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/newlib/stdatomic.c b/components/newlib/stdatomic.c index 6089efd9ee..f41e9668cd 100644 --- a/components/newlib/stdatomic.c +++ b/components/newlib/stdatomic.c @@ -207,7 +207,7 @@ CLANG_DECLARE_ALIAS( __sync_val_compare_and_swap_ ## n ) *ptr = val; \ _ATOMIC_EXIT_CRITICAL(state); \ return ret; \ -} +} \ CLANG_DECLARE_ALIAS( __sync_lock_test_and_set_ ## n ) #define SYNC_LOCK_RELEASE(n, type) void CLANG_ATOMIC_SUFFIX(__sync_lock_release_ ## n) (type *ptr) \ @@ -215,7 +215,7 @@ CLANG_DECLARE_ALIAS( __sync_lock_test_and_set_ ## n ) unsigned state = _ATOMIC_ENTER_CRITICAL(); \ *ptr = 0; \ _ATOMIC_EXIT_CRITICAL(state); \ -} +} \ CLANG_DECLARE_ALIAS( __sync_lock_release_ ## n )