Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parser: Add frontend support for C11 atomics #582

Merged
merged 1 commit into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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" \

Loading