diff options
Diffstat (limited to 'tinycc/lib/stdatomic.c')
| -rw-r--r-- | tinycc/lib/stdatomic.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/tinycc/lib/stdatomic.c b/tinycc/lib/stdatomic.c new file mode 100644 index 0000000..bd8c0ce --- /dev/null +++ b/tinycc/lib/stdatomic.c @@ -0,0 +1,166 @@ +// for libtcc1, avoid including files that are not part of tcc +// #include <stdint.h> +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned int +#define uint64_t unsigned long long +#define bool _Bool +#define false 0 +#define true 1 +#define __ATOMIC_RELAXED 0 +#define __ATOMIC_CONSUME 1 +#define __ATOMIC_ACQUIRE 2 +#define __ATOMIC_RELEASE 3 +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_SEQ_CST 5 +typedef __SIZE_TYPE__ size_t; + +#if defined __i386__ || defined __x86_64__ +#define ATOMIC_COMPARE_EXCHANGE(TYPE, MODE, SUFFIX) \ + bool __atomic_compare_exchange_##MODE \ + (volatile void *atom, void *ref, TYPE xchg, \ + bool weak, int success_memorder, int failure_memorder) \ + { \ + TYPE rv; \ + TYPE cmp = *(TYPE *)ref; \ + __asm__ volatile( \ + "lock cmpxchg" SUFFIX " %2,%1\n" \ + : "=a" (rv), "+m" (*(TYPE *)atom) \ + : "q" (xchg), "0" (cmp) \ + : "memory" \ + ); \ + *(TYPE *)ref = rv; \ + return (rv == cmp); \ + } +#else +#define ATOMIC_COMPARE_EXCHANGE(TYPE, MODE, SUFFIX) \ + extern bool __atomic_compare_exchange_##MODE \ + (volatile void *atom, void *ref, TYPE xchg, \ + bool weak, int success_memorder, int failure_memorder); +#endif + +#define ATOMIC_LOAD(TYPE, MODE) \ + TYPE __atomic_load_##MODE(const volatile void *atom, int memorder) \ + { \ + return *(volatile TYPE *)atom; \ + } + +#define ATOMIC_STORE(TYPE, MODE) \ + void __atomic_store_##MODE(volatile void *atom, TYPE value, int memorder) \ + { \ + *(volatile TYPE *)atom = value; \ + } + +#define ATOMIC_GEN_OP(TYPE, MODE, NAME, OP, RET) \ + TYPE __atomic_##NAME##_##MODE(volatile void *atom, TYPE value, int memorder) \ + { \ + TYPE xchg, cmp; \ + __atomic_load((TYPE *)atom, (TYPE *)&cmp, __ATOMIC_RELAXED); \ + do { \ + xchg = (OP); \ + } while (!__atomic_compare_exchange((TYPE *)atom, &cmp, &xchg, true, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); \ + return RET; \ + } + +#define ATOMIC_EXCHANGE(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, exchange, value, cmp) +#define ATOMIC_ADD_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, add_fetch, (cmp + value), xchg) +#define ATOMIC_SUB_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, sub_fetch, (cmp - value), xchg) +#define ATOMIC_AND_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, and_fetch, (cmp & value), xchg) +#define ATOMIC_OR_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, or_fetch, (cmp | value), xchg) +#define ATOMIC_XOR_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, xor_fetch, (cmp ^ value), xchg) +#define ATOMIC_NAND_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, nand_fetch, ~(cmp & value), xchg) +#define ATOMIC_FETCH_ADD(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_add, (cmp + value), cmp) +#define ATOMIC_FETCH_SUB(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_sub, (cmp - value), cmp) +#define ATOMIC_FETCH_AND(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_and, (cmp & value), cmp) +#define ATOMIC_FETCH_OR(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_or, (cmp | value), cmp) +#define ATOMIC_FETCH_XOR(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_xor, (cmp ^ value), cmp) +#define ATOMIC_FETCH_NAND(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_nand, ~(cmp & value), cmp) + +#define ATOMIC_GEN(TYPE, SIZE, SUFFIX) \ + ATOMIC_STORE(TYPE, SIZE) \ + ATOMIC_LOAD(TYPE, SIZE) \ + ATOMIC_COMPARE_EXCHANGE(TYPE, SIZE, SUFFIX) \ + ATOMIC_EXCHANGE(TYPE, SIZE) \ + ATOMIC_ADD_FETCH(TYPE, SIZE) \ + ATOMIC_SUB_FETCH(TYPE, SIZE) \ + ATOMIC_AND_FETCH(TYPE, SIZE) \ + ATOMIC_OR_FETCH(TYPE, SIZE) \ + ATOMIC_XOR_FETCH(TYPE, SIZE) \ + ATOMIC_NAND_FETCH(TYPE, SIZE) \ + ATOMIC_FETCH_ADD(TYPE, SIZE) \ + ATOMIC_FETCH_SUB(TYPE, SIZE) \ + ATOMIC_FETCH_AND(TYPE, SIZE) \ + ATOMIC_FETCH_OR(TYPE, SIZE) \ + ATOMIC_FETCH_XOR(TYPE, SIZE) \ + ATOMIC_FETCH_NAND(TYPE, SIZE) + +ATOMIC_GEN(uint8_t, 1, "b") +ATOMIC_GEN(uint16_t, 2, "w") +ATOMIC_GEN(uint32_t, 4, "l") +#if defined __x86_64__ || defined __aarch64__ || defined __riscv +ATOMIC_GEN(uint64_t, 8, "q") +#endif + +/* uses alias to allow building with gcc/clang */ +#ifdef __TINYC__ +#define ATOMIC(x) __atomic_##x +#else +#define ATOMIC(x) __tcc_atomic_##x +#endif + +void ATOMIC(signal_fence) (int memorder) +{ +} + +void ATOMIC(thread_fence) (int memorder) +{ +#if defined __i386__ + __asm__ volatile("lock orl $0, (%esp)"); +#elif defined __x86_64__ + __asm__ volatile("lock orq $0, (%rsp)"); +#elif defined __arm__ + __asm__ volatile(".int 0xee070fba"); // mcr p15, 0, r0, c7, c10, 5 +#elif defined __aarch64__ + __asm__ volatile(".int 0xd5033bbf"); // dmb ish +#elif defined __riscv + __asm__ volatile(".int 0x0ff0000f"); // fence iorw,iorw +#endif +} + +bool ATOMIC(is_lock_free) (unsigned long size, const volatile void *ptr) +{ + bool ret; + + switch (size) { + case 1: ret = true; break; + case 2: ret = true; break; + case 4: ret = true; break; +#if defined __x86_64__ || defined __aarch64__ || defined __riscv + case 8: ret = true; break; +#else + case 8: ret = false; break; +#endif + default: ret = false; break; + } + return ret; +} + +#ifndef __TINYC__ +void __atomic_signal_fence(int memorder) __attribute__((alias("__tcc_atomic_signal_fence"))); +void __atomic_thread_fence(int memorder) __attribute__((alias("__tcc_atomic_thread_fence"))); +bool __atomic_is_lock_free(unsigned long size, const volatile void *ptr) __attribute__((alias("__tcc_atomic_is_lock_free"))); +#endif |
