Skip to content

Commit

Permalink
Parser: Add frontend support for C11 atomics
Browse files Browse the repository at this point in the history
  • Loading branch information
ehaas authored and Vexu committed Nov 26, 2023
1 parent 9d538ea commit 75af1f9
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 11 deletions.
77 changes: 73 additions & 4 deletions include/stdatomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

#pragma once

/* Todo: Set to 202311L once header is compliant with C23 */
#define __STDC_VERSION_STDATOMIC_H__ 0
// TODO finish atomics support

#define __STDC_VERSION_STDATOMIC_H__ 202311L

#if __STDC_HOSTED__ && __has_include_next(<stdatomic.h>)
#include_next <stdatomic.h>
Expand All @@ -14,6 +11,27 @@
#include <stddef.h>
#include <stdint.h>

#define ATOMIC_BOOL_LOCK_FREE __ATOMIC_BOOL_LOCK_FREE
#define ATOMIC_CHAR_LOCK_FREE __ATOMIC_CHAR_LOCK_FREE
#define ATOMIC_CHAR16_T_LOCK_FREE __ATOMIC_CHAR16_T_LOCK_FREE
#define ATOMIC_CHAR32_T_LOCK_FREE __ATOMIC_CHAR32_T_LOCK_FREE
#define ATOMIC_WCHAR_T_LOCK_FREE __ATOMIC_WCHAR_T_LOCK_FREE
#define ATOMIC_SHORT_LOCK_FREE __ATOMIC_SHORT_LOCK_FREE
#define ATOMIC_INT_LOCK_FREE __ATOMIC_INT_LOCK_FREE
#define ATOMIC_LONG_LOCK_FREE __ATOMIC_LONG_LOCK_FREE
#define ATOMIC_LLONG_LOCK_FREE __ATOMIC_LLONG_LOCK_FREE
#define ATOMIC_POINTER_LOCK_FREE __ATOMIC_POINTER_LOCK_FREE
#if defined(__ATOMIC_CHAR8_T_LOCK_FREE)
#define ATOMIC_CHAR8_T_LOCK_FREE __ATOMIC_CHAR8_T_LOCK_FREE
#endif

#if __STDC_VERSION__ < 202311L
/* ATOMIC_VAR_INIT was removed in C23 */
#define ATOMIC_VAR_INIT(value) (value)
#endif

#define atomic_init __c11_atomic_init

typedef enum memory_order {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
Expand All @@ -23,6 +41,16 @@ typedef enum memory_order {
memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;

#define kill_dependency(y) (y)

void atomic_thread_fence(memory_order);
void atomic_signal_fence(memory_order);

#define atomic_thread_fence(order) __c11_atomic_thread_fence(order)
#define atomic_signal_fence(order) __c11_atomic_signal_fence(order)

#define atomic_is_lock_free(obj) __c11_atomic_is_lock_free(sizeof(*(obj)))

typedef _Atomic(_Bool) atomic_bool;
typedef _Atomic(char) atomic_char;
typedef _Atomic(signed char) atomic_schar;
Expand Down Expand Up @@ -61,9 +89,50 @@ typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
typedef _Atomic(intmax_t) atomic_intmax_t;
typedef _Atomic(uintmax_t) atomic_uintmax_t;

#define atomic_store(object, desired) __c11_atomic_store(object, desired, __ATOMIC_SEQ_CST)
#define atomic_store_explicit __c11_atomic_store

#define atomic_load(object) __c11_atomic_load(object, __ATOMIC_SEQ_CST)
#define atomic_load_explicit __c11_atomic_load

#define atomic_exchange(object, desired) __c11_atomic_exchange(object, desired, __ATOMIC_SEQ_CST)
#define atomic_exchange_explicit __c11_atomic_exchange

#define atomic_compare_exchange_strong(object, expected, desired) __c11_atomic_compare_exchange_strong(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_strong_explicit __c11_atomic_compare_exchange_strong

#define atomic_compare_exchange_weak(object, expected, desired) __c11_atomic_compare_exchange_weak(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
#define atomic_compare_exchange_weak_explicit __c11_atomic_compare_exchange_weak

#define atomic_fetch_add(object, operand) __c11_atomic_fetch_add(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit __c11_atomic_fetch_add

#define atomic_fetch_sub(object, operand) __c11_atomic_fetch_sub(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit __c11_atomic_fetch_sub

#define atomic_fetch_or(object, operand) __c11_atomic_fetch_or(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit __c11_atomic_fetch_or

#define atomic_fetch_xor(object, operand) __c11_atomic_fetch_xor(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit __c11_atomic_fetch_xor

#define atomic_fetch_and(object, operand) __c11_atomic_fetch_and(object, operand, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit __c11_atomic_fetch_and

typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;

#define ATOMIC_FLAG_INIT { 0 }

_Bool atomic_flag_test_and_set(volatile atomic_flag *);
_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order);
void atomic_flag_clear(volatile atomic_flag *);
void atomic_flag_clear_explicit(volatile atomic_flag *, memory_order);

#define atomic_flag_test_and_set(object) __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST)
#define atomic_flag_test_and_set_explicit(object, order) __c11_atomic_exchange(&(object)->_Value, 1, order)

#define atomic_flag_clear(object) __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST)
#define atomic_flag_clear_explicit(object, order) __c11_atomic_store(&(object)->_Value, 0, order)


#endif
6 changes: 6 additions & 0 deletions src/aro/Builtins/Builtin.def
Original file line number Diff line number Diff line change
Expand Up @@ -17009,6 +17009,8 @@ wmemmove
.header = .wchar
.attributes = .{ .lib_function_without_prefix = true, .const_evaluable = true }

# C11 _Atomic operations for <stdatomic.h>.

__c11_atomic_init
.param_str = "v."
.attributes = .{ .custom_typecheck = true }
Expand Down Expand Up @@ -17065,6 +17067,8 @@ __c11_atomic_fetch_min
.param_str = "v."
.attributes = .{ .custom_typecheck = true }

# GNU atomics

__atomic_load
.param_str = "v."
.attributes = .{ .custom_typecheck = true }
Expand Down Expand Up @@ -17153,6 +17157,8 @@ __atomic_nand_fetch
.param_str = "v."
.attributes = .{ .custom_typecheck = true }

# clang extensions

__atomic_fetch_min
.param_str = "v."
.attributes = .{ .custom_typecheck = true }
Expand Down
20 changes: 19 additions & 1 deletion src/aro/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,25 @@ fn generateSystemDefines(comp: *Compilation, w: anytype) !void {
\\
);

// TODO: Set these to target-specific constants depending on backend capabilities
// For now they are just set to the "may be lock-free" value
try w.writeAll(
\\#define __ATOMIC_BOOL_LOCK_FREE 1
\\#define __ATOMIC_CHAR_LOCK_FREE 1
\\#define __ATOMIC_CHAR16_T_LOCK_FREE 1
\\#define __ATOMIC_CHAR32_T_LOCK_FREE 1
\\#define __ATOMIC_WCHAR_T_LOCK_FREE 1
\\#define __ATOMIC_SHORT_LOCK_FREE 1
\\#define __ATOMIC_INT_LOCK_FREE 1
\\#define __ATOMIC_LONG_LOCK_FREE 1
\\#define __ATOMIC_LLONG_LOCK_FREE 1
\\#define __ATOMIC_POINTER_LOCK_FREE 1
\\
);
if (comp.langopts.hasChar8_T()) {
try w.writeAll("#define __ATOMIC_CHAR8_T_LOCK_FREE 1\n");
}

// types
if (comp.getCharSignedness() == .unsigned) try w.writeAll("#define __CHAR_UNSIGNED__ 1\n");
try w.writeAll("#define __CHAR_BIT__ 8\n");
Expand Down Expand Up @@ -518,7 +537,6 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi

// standard macros
try buf.appendSlice(
\\#define __STDC_NO_ATOMICS__ 1
\\#define __STDC_NO_COMPLEX__ 1
\\#define __STDC_NO_THREADS__ 1
\\#define __STDC_NO_VLA__ 1
Expand Down
72 changes: 68 additions & 4 deletions src/aro/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4823,16 +4823,41 @@ const CallExpr = union(enum) {
return switch (self) {
.standard => null,
.builtin => |builtin| switch (builtin.tag) {
Builtin.tagFromName("__builtin_complex").? => 2,

Builtin.tagFromName("__c11_atomic_thread_fence").?,
Builtin.tagFromName("__c11_atomic_signal_fence").?,
Builtin.tagFromName("__c11_atomic_is_lock_free").?,
=> 1,

Builtin.tagFromName("__builtin_complex").?,
Builtin.tagFromName("__c11_atomic_load").?,
Builtin.tagFromName("__c11_atomic_init").?,
=> 2,

Builtin.tagFromName("__c11_atomic_store").?,
Builtin.tagFromName("__c11_atomic_exchange").?,
Builtin.tagFromName("__c11_atomic_fetch_add").?,
Builtin.tagFromName("__c11_atomic_fetch_sub").?,
Builtin.tagFromName("__c11_atomic_fetch_or").?,
Builtin.tagFromName("__c11_atomic_fetch_xor").?,
Builtin.tagFromName("__c11_atomic_fetch_and").?,
Builtin.tagFromName("__atomic_fetch_add").?,
Builtin.tagFromName("__atomic_fetch_sub").?,
Builtin.tagFromName("__atomic_fetch_and").?,
Builtin.tagFromName("__atomic_fetch_xor").?,
Builtin.tagFromName("__atomic_fetch_or").?,
Builtin.tagFromName("__atomic_fetch_nand").?,
Builtin.tagFromName("__atomic_add_fetch").?,
Builtin.tagFromName("__atomic_sub_fetch").?,
Builtin.tagFromName("__atomic_and_fetch").?,
Builtin.tagFromName("__atomic_xor_fetch").?,
Builtin.tagFromName("__atomic_or_fetch").?,
Builtin.tagFromName("__atomic_nand_fetch").?,
=> 3,

Builtin.tagFromName("__c11_atomic_compare_exchange_strong").?,
Builtin.tagFromName("__c11_atomic_compare_exchange_weak").?,
=> 5,

Builtin.tagFromName("__atomic_compare_exchange").?,
Builtin.tagFromName("__atomic_compare_exchange_n").?,
=> 6,
Expand All @@ -4845,15 +4870,45 @@ const CallExpr = union(enum) {
return switch (self) {
.standard => callable_ty.returnType(),
.builtin => |builtin| switch (builtin.tag) {
Builtin.tagFromName("__c11_atomic_exchange").? => {
if (p.list_buf.items.len != 4) return Type.invalid; // wrong number of arguments; already an error
const second_param = p.list_buf.items[2];
return p.nodes.items(.ty)[@intFromEnum(second_param)];
},
Builtin.tagFromName("__c11_atomic_load").? => {
if (p.list_buf.items.len != 3) return Type.invalid; // wrong number of arguments; already an error
const first_param = p.list_buf.items[1];
const ty = p.nodes.items(.ty)[@intFromEnum(first_param)];
if (!ty.isPtr()) return Type.invalid;
return ty.elemType();
},

Builtin.tagFromName("__atomic_fetch_add").?,
Builtin.tagFromName("__atomic_add_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_add").?,

Builtin.tagFromName("__atomic_fetch_sub").?,
Builtin.tagFromName("__atomic_sub_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_sub").?,

Builtin.tagFromName("__atomic_fetch_and").?,
Builtin.tagFromName("__atomic_and_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_and").?,

Builtin.tagFromName("__atomic_fetch_xor").?,
Builtin.tagFromName("__atomic_xor_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_xor").?,

Builtin.tagFromName("__atomic_fetch_or").?,
Builtin.tagFromName("__atomic_or_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_or").?,

Builtin.tagFromName("__atomic_fetch_nand").?,
Builtin.tagFromName("__atomic_nand_fetch").?,
Builtin.tagFromName("__c11_atomic_fetch_nand").?,
=> {
if (p.list_buf.items.len < 2) return Type.invalid; // not enough arguments; already an error
const second_param = p.list_buf.items[p.list_buf.items.len - 2];
if (p.list_buf.items.len != 3) return Type.invalid; // wrong number of arguments; already an error
const second_param = p.list_buf.items[2];
return p.nodes.items(.ty)[@intFromEnum(second_param)];
},
Builtin.tagFromName("__builtin_complex").? => {
Expand All @@ -4863,8 +4918,17 @@ const CallExpr = union(enum) {
},
Builtin.tagFromName("__atomic_compare_exchange").?,
Builtin.tagFromName("__atomic_compare_exchange_n").?,
Builtin.tagFromName("__c11_atomic_is_lock_free").?,
=> .{ .specifier = .bool },
else => callable_ty.returnType(),

Builtin.tagFromName("__c11_atomic_compare_exchange_strong").?,
Builtin.tagFromName("__c11_atomic_compare_exchange_weak").?,
=> {
if (p.list_buf.items.len != 6) return Type.invalid; // wrong number of arguments
const third_param = p.list_buf.items[3];
return p.nodes.items(.ty)[@intFromEnum(third_param)];
},
},
};
}
Expand Down
57 changes: 55 additions & 2 deletions test/cases/atomic builtins.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//aro-args -std=c23
#include <stdbool.h>
#include <stdatomic.h>

Expand All @@ -20,8 +21,60 @@ void foo(void) {

char a = 0, old;
old = __atomic_fetch_add(&a, 1, memory_order_relaxed);

_Atomic int i;
atomic_init(&i, 42);

i = kill_dependency(i);

atomic_thread_fence(memory_order_relaxed);
atomic_signal_fence(memory_order_seq_cst);

res = atomic_is_lock_free(&i);

atomic_store(&i, 42);
atomic_store_explicit(&i, 42, memory_order_relaxed);

x = atomic_load(&i);
x = atomic_load_explicit(&i, memory_order_acquire);

x = atomic_exchange(&i, 42);
x = atomic_exchange_explicit(&i, 42, memory_order_release);

x = atomic_compare_exchange_strong(&i, &y, 32);
x = atomic_compare_exchange_strong_explicit(&i, &y, 32, memory_order_acq_rel, memory_order_consume);

x = atomic_compare_exchange_weak(&i, &y, 32);
x = atomic_compare_exchange_weak_explicit(&i, &y, 32, memory_order_acq_rel, memory_order_consume);

x = atomic_fetch_add(&i, 42);
x = atomic_fetch_add_explicit(&i, 42, memory_order_relaxed);

x = atomic_fetch_sub(&i, 42);
x = atomic_fetch_sub_explicit(&i, 42, memory_order_relaxed);

x = atomic_fetch_or(&i, 42);
x = atomic_fetch_or_explicit(&i, 42, memory_order_relaxed);

x = atomic_fetch_xor(&i, 42);
x = atomic_fetch_xor_explicit(&i, 42, memory_order_relaxed);

x = atomic_fetch_and(&i, 42);
x = atomic_fetch_and_explicit(&i, 42, memory_order_relaxed);

atomic_flag flag = ATOMIC_FLAG_INIT;
res = atomic_flag_test_and_set(&flag);
res = atomic_flag_test_and_set_explicit(&flag, memory_order_seq_cst);
atomic_flag_clear(&flag);
atomic_flag_clear_explicit(&flag, memory_order_seq_cst);
atomic_flag_clear_explicit(&flag, memory_order_seq_cst);
}

#define EXPECTED_ERRORS "atomic builtins.c:16:33: error: expected 6 argument(s) got 2" \
"atomic builtins.c:19:31: error: expected 6 argument(s) got 2" \
/* subject to change */
_Static_assert(ATOMIC_CHAR8_T_LOCK_FREE == 1, "");
_Static_assert(ATOMIC_POINTER_LOCK_FREE == 1, "");


#define EXPECTED_ERRORS "atomic builtins.c:17:33: error: expected 6 argument(s) got 2" \
"atomic builtins.c:20:31: error: expected 6 argument(s) got 2" \

0 comments on commit 75af1f9

Please sign in to comment.