Here is an alternative way to implement support for atomics, this time via inline assembler and macros (ab)use. The main idea is that we can implement most of the atomic operations via cmpxchg. The only exception is atomic_load routine, which should not update the value, according to its prototype. This patch also makes such an assumption for atomic_store routine, even though it is possible to implement it via cmpxchg as well. With this patch, most of the atomic routines are generated in an architecture-agnostic way, unless some specific architecture explicitly desires to override such behavior.
This patch provides a simple and somewhat hacky implementation for i386; x86_64 basically takes this implementation and adds three routines atop of it. I'd like to discuss this code; there are parts I don't particularly like, for example, the inclusion of C source code via preprocessor directives. Ideas and remarks are welcome, as usual. --- lib/Makefile | 4 ++-- lib/atomic-gen32.c | 25 +++++++++++++++++++++++++ lib/atomic-gen64.c | 8 ++++++++ lib/atomic-i386.c | 19 +++++++++++++++++++ lib/atomic-x86.h | 31 +++++++++++++++++++++++++++++++ lib/atomic-x86_64.c | 12 ++++++++++++ lib/atomic.h | 39 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 lib/atomic-gen32.c create mode 100644 lib/atomic-gen64.c create mode 100644 lib/atomic-i386.c create mode 100644 lib/atomic-x86.h create mode 100644 lib/atomic-x86_64.c create mode 100644 lib/atomic.h diff --git a/lib/Makefile b/lib/Makefile index 9121d33..fe2fd32 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,8 +42,8 @@ $(X)BT_O += tcov.o DSO_O = dsohandle.o -I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BT_O) -X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BT_O) +I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BT_O) atomic-i386.o +X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BT_O) atomic-x86_64.o ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o fetch_and_add_arm.o $(BT_O) ARM64_O = lib-arm64.o fetch_and_add_arm64.o $(BT_O) RISCV64_O = lib-arm64.o fetch_and_add_riscv64.o $(BT_O) diff --git a/lib/atomic-gen32.c b/lib/atomic-gen32.c new file mode 100644 index 0000000..64575cd --- /dev/null +++ b/lib/atomic-gen32.c @@ -0,0 +1,25 @@ +#include "atomic.h" + +ATOMIC_EXCHANGE(uint8_t, 1) +ATOMIC_EXCHANGE(uint16_t, 2) +ATOMIC_EXCHANGE(uint32_t, 4) + +ATOMIC_FETCH_ADD(uint8_t, 1) +ATOMIC_FETCH_ADD(uint16_t, 2) +ATOMIC_FETCH_ADD(uint32_t, 4) + +ATOMIC_FETCH_SUB(uint8_t, 1) +ATOMIC_FETCH_SUB(uint16_t, 2) +ATOMIC_FETCH_SUB(uint32_t, 4) + +ATOMIC_FETCH_AND(uint8_t, 1) +ATOMIC_FETCH_AND(uint16_t, 2) +ATOMIC_FETCH_AND(uint32_t, 4) + +ATOMIC_FETCH_OR(uint8_t, 1) +ATOMIC_FETCH_OR(uint16_t, 2) +ATOMIC_FETCH_OR(uint32_t, 4) + +ATOMIC_FETCH_XOR(uint8_t, 1) +ATOMIC_FETCH_XOR(uint16_t, 2) +ATOMIC_FETCH_XOR(uint32_t, 4) diff --git a/lib/atomic-gen64.c b/lib/atomic-gen64.c new file mode 100644 index 0000000..edd8263 --- /dev/null +++ b/lib/atomic-gen64.c @@ -0,0 +1,8 @@ +#include "atomic.h" + +ATOMIC_EXCHANGE(uint64_t, 8) +ATOMIC_FETCH_ADD(uint64_t, 8) +ATOMIC_FETCH_SUB(uint64_t, 8) +ATOMIC_FETCH_AND(uint64_t, 8) +ATOMIC_FETCH_OR(uint64_t, 8) +ATOMIC_FETCH_XOR(uint64_t, 8) diff --git a/lib/atomic-i386.c b/lib/atomic-i386.c new file mode 100644 index 0000000..ca8b2a4 --- /dev/null +++ b/lib/atomic-i386.c @@ -0,0 +1,19 @@ +#pragma once + +#include <stdint.h> + +#include "atomic-x86.h" + +ATOMIC_X86_STORE(uint8_t, 1) +ATOMIC_X86_STORE(uint16_t, 2) +ATOMIC_X86_STORE(uint32_t, 4) + +ATOMIC_X86_LOAD(uint8_t, 1) +ATOMIC_X86_LOAD(uint16_t, 2) +ATOMIC_X86_LOAD(uint32_t, 4) + +ATOMIC_X86_COMPARE_EXCHANGE(uint8_t, 1, "b") +ATOMIC_X86_COMPARE_EXCHANGE(uint16_t, 2, "w") +ATOMIC_X86_COMPARE_EXCHANGE(uint32_t, 4, "l") + +#include "atomic-gen32.c" diff --git a/lib/atomic-x86.h b/lib/atomic-x86.h new file mode 100644 index 0000000..ad4e1f0 --- /dev/null +++ b/lib/atomic-x86.h @@ -0,0 +1,31 @@ +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#define ATOMIC_X86_COMPARE_EXCHANGE(TYPE, MODE, SUFFIX) \ + bool __atomic_compare_exchange_##MODE(_Atomic(TYPE) *atom, TYPE *ref, TYPE xchg) \ + { \ + TYPE rv; \ + TYPE cmp = *ref; \ + asm volatile( \ + "lock cmpxchg" SUFFIX " %2,%1\n" \ + : "=a" (rv), "+m" (*atom) \ + : "q" (xchg), "0" (cmp) \ + : "memory" \ + ); \ + *ref = rv; \ + return (rv == cmp); \ + } + +#define ATOMIC_X86_LOAD(TYPE, MODE) \ + TYPE __atomic_load_##MODE(const _Atomic(TYPE) *atom) \ + { \ + return *(volatile TYPE *)atom; \ + } + +#define ATOMIC_X86_STORE(TYPE, MODE) \ + void __atomic_store_##MODE(_Atomic(TYPE) *atom, TYPE value) \ + { \ + *(volatile TYPE *)atom = value; \ + } diff --git a/lib/atomic-x86_64.c b/lib/atomic-x86_64.c new file mode 100644 index 0000000..974615d --- /dev/null +++ b/lib/atomic-x86_64.c @@ -0,0 +1,12 @@ +#pragma once + +#include <stdint.h> + +#include "atomic-x86.h" +#include "atomic-i386.c" + +ATOMIC_X86_STORE(uint64_t, 8) +ATOMIC_X86_LOAD(uint64_t, 8) +ATOMIC_X86_COMPARE_EXCHANGE(uint64_t, 8, "q") + +#include "atomic-gen64.c" diff --git a/lib/atomic.h b/lib/atomic.h new file mode 100644 index 0000000..6c9190a --- /dev/null +++ b/lib/atomic.h @@ -0,0 +1,39 @@ +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#define ATOMIC_GEN_OP(TYPE, MODE, NAME, OP) \ + TYPE __atomic_##NAME##_##MODE(_Atomic(TYPE) *atom, TYPE value) \ + { \ + TYPE xchg; \ + TYPE cmp = __atomic_load(atom, __ATOMIC_RELAXED); \ + do { \ + xchg = (OP); \ + } while (!__atomic_compare_exchange(atom, &cmp, xchg, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); \ + return cmp; \ + } + +#ifndef ATOMIC_EXCHANGE +# define ATOMIC_EXCHANGE(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, exchange, value) +#endif + +#ifndef ATOMIC_FETCH_ADD +# define ATOMIC_FETCH_ADD(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, fetch_add, (cmp + value)) +#endif + +#ifndef ATOMIC_FETCH_SUB +# define ATOMIC_FETCH_SUB(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, fetch_sub, (cmp - value)) +#endif + +#ifndef ATOMIC_FETCH_AND +# define ATOMIC_FETCH_AND(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, fetch_and, (cmp & value)) +#endif + +#ifndef ATOMIC_FETCH_OR +# define ATOMIC_FETCH_OR(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, fetch_or, (cmp | value)) +#endif + +#ifndef ATOMIC_FETCH_XOR +# define ATOMIC_FETCH_XOR(TYPE, MODE) ATOMIC_GEN_OP(TYPE, MODE, fetch_xor, (cmp ^ value)) +#endif -- 2.30.1 _______________________________________________ Tinycc-devel mailing list Tinycc-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/tinycc-devel