Skip to content

Commit ad35f1b

Browse files
committed
Use compiler intrinsics instead
1 parent 78f2cc6 commit ad35f1b

File tree

5 files changed

+149
-91
lines changed

5 files changed

+149
-91
lines changed

Zend/zend_atomic.c

Lines changed: 17 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,52 +14,27 @@
1414

1515
#include "zend_atomic.h"
1616

17-
/* The general strategy here is to use C11's stdatomic.h functions when they
18-
* are available, and then fall-back to non-atomic operations, except for
19-
* Windows, which has C++ std::atomic_bool but not C atomic_bool.
20-
*
21-
* The defines HAVE_ATOMIC_BOOL should only be set when it is determined that
22-
* representations of atomic_bool and bool are the same, as this is relied on
23-
* in the implementation.
17+
/* This file contains the non-inline copy of atomic functions. This is useful
18+
* for extensions written in languages such as Rust. C and C++ compilers are
19+
* probably going to inline these functions, but in the case they don't, this
20+
* is also where the code will go.
2421
*/
2522

26-
#if HAVE_STDATOMIC_H
27-
28-
#include <stdatomic.h>
29-
30-
ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
31-
return atomic_exchange((atomic_bool *)obj, desired);
32-
}
33-
34-
ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
35-
return atomic_load((const atomic_bool *)obj);
36-
}
37-
38-
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
39-
atomic_store((atomic_bool *)obj, desired);
40-
}
41-
42-
#else
43-
44-
/* Yes, these are not guaranteed to be atomic. Understand that previously
45-
* atomics were never used, so the fact they are sometimes used is an
46-
* improvement. As more platforms support C11 atomics, or as we add support
47-
* for more platforms through intrinsics/asm, this should be used less and
48-
* less until it can be removed.
23+
/* Defined for FFI users; everyone else use ZEND_ATOMIC_BOOL_INIT.
24+
* This is NOT ATOMIC as it is meant for initialization.
4925
*/
50-
51-
ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
52-
bool previous = obj->bytes;
53-
obj->bytes = desired;
54-
return previous;
26+
ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired) {
27+
ZEND_ATOMIC_BOOL_INIT(obj, desired);
5528
}
5629

57-
ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
58-
return obj->bytes;
59-
}
60-
61-
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
62-
obj->bytes = desired;
63-
}
30+
extern inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj,
31+
bool desired);
32+
extern inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj,
33+
bool desired);
6434

35+
#if ZEND_WIN32
36+
/* On this platform it is non-const due to Iterlocked API*/
37+
extern inline ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj);
38+
#else
39+
extern inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj);
6540
#endif

Zend/zend_atomic.cpp

Lines changed: 0 additions & 37 deletions
This file was deleted.

Zend/zend_atomic.h

Lines changed: 129 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,143 @@
1919

2020
#include <stdbool.h>
2121

22-
/* These functions correspond to C11's stdatomic.h functions but are type-
23-
* specialized because it's difficult to provide portable routines such as
24-
* exchange without making functions.
25-
*
26-
* Treat zend_atomic_* types as opaque. They have definitions only for size
22+
#ifndef __has_feature
23+
#define __has_feature(x) 0
24+
#endif
25+
26+
/* Builtins are used to avoid library linkage */
27+
#if __has_feature(c_atomic)
28+
#define HAVE_C11_ATOMICS 1
29+
#elif __GNUC_PREREQ__(4, 7)
30+
#define HAVE_GNUC_ATOMICS 1
31+
#elif defined(__GNUC__)
32+
#define HAVE_SYNC_ATOMICS 1
33+
#elif !defined(ZEND_WIN32)
34+
#define HAVE_NO_ATOMICS 1
35+
#endif
36+
37+
/* Treat zend_atomic_* types as opaque. They have definitions only for size
2738
* and alignment purposes.
2839
*/
2940

41+
#if ZEND_WIN32
42+
typedef struct zend_atomic_bool_s {
43+
volatile char value;
44+
} zend_atomic_bool;
45+
#elif HAVE_C11_ATOMICS
46+
typedef struct zend_atomic_bool_s {
47+
_Atomic(bool) value;
48+
} zend_atomic_bool;
49+
#else
3050
typedef struct zend_atomic_bool_s {
31-
volatile bool bytes;
51+
volatile bool value;
3252
} zend_atomic_bool;
53+
#endif
3354

3455
BEGIN_EXTERN_C()
3556

36-
ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj);
37-
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired);
38-
ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired);
57+
#if ZEND_WIN32
58+
59+
#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
60+
61+
inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
62+
return InterlockedExchange8(&obj->value, desired);
63+
}
64+
65+
/* On this platform it is non-const due to Iterlocked API*/
66+
inline ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj) {
67+
/* Or'ing with false won't change the value. */
68+
return InterlockedOr8(&obj->value, false);
69+
}
70+
71+
inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
72+
(void)InterlockedExchange8(&obj->value, desired);
73+
}
74+
75+
#elif HAVE_C11_ATOMICS
76+
77+
#define ZEND_ATOMIC_BOOL_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired))
78+
79+
inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
80+
return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
81+
}
82+
83+
inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
84+
return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST);
85+
}
86+
87+
inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
88+
__c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
89+
}
90+
91+
#elif HAVE_GNUC_ATOMICS
92+
93+
#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
94+
95+
inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
96+
return __atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
97+
}
98+
99+
inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
100+
return __atomic_load(&obj->value, __ATOMIC_SEQ_CST);
101+
}
102+
103+
inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
104+
__atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
105+
}
106+
107+
#elif HAVE_SYNC_ATOMICS
108+
109+
#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
110+
111+
inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
112+
bool prev = __sync_lock_test_and_set(&obj->value, desired);
113+
114+
/* __sync_lock_test_and_set only does an acquire barrier, so sync
115+
* immediately after.
116+
*/
117+
__sync_synchronize();
118+
return prev;
119+
}
120+
121+
inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
122+
/* Or'ing false won't change the value */
123+
return __sync_fetch_and_or(&obj->value, false);
124+
}
125+
126+
inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
127+
__sync_synchronize();
128+
obj->value = desired;
129+
__sync_synchronize();
130+
}
131+
132+
#elif HAVE_NO_ATOMICS
133+
134+
#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
135+
136+
/* Yes, these are not guaranteed to be atomic. Understand that previously
137+
* atomics were never used, so the fact they are sometimes used is an
138+
* improvement. As more platforms support C11 atomics, or as we add support
139+
* for more platforms through intrinsics/asm, this should be used less and
140+
* less until it can be removed.
141+
*/
142+
143+
inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
144+
obj->value = desired;
145+
}
146+
147+
inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
148+
return obj->value;
149+
}
150+
151+
inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
152+
bool prev = obj->value;
153+
obj->value = true;
154+
return prev;
155+
}
156+
157+
#error Just seeing if any platforms in CI are using this fallback.
158+
#endif
39159

40160
END_EXTERN_C()
41161

Zend/zend_execute_API.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ void init_executor(void) /* {{{ */
168168
zend_objects_store_init(&EG(objects_store), 1024);
169169

170170
EG(full_tables_cleanup) = 0;
171-
zend_atomic_bool_store(&EG(vm_interrupt), false);
172-
zend_atomic_bool_store(&EG(timed_out), false);
171+
ZEND_ATOMIC_BOOL_INIT(&EG(vm_interrupt), false);
172+
ZEND_ATOMIC_BOOL_INIT(&EG(timed_out), false);
173173

174174
EG(exception) = NULL;
175175
EG(prev_exception) = NULL;

win32/build/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \
238238
zend_default_classes.c zend_execute.c zend_strtod.c zend_gc.c zend_closures.c zend_weakrefs.c \
239239
zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \
240240
zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \
241-
zend_enum.c zend_fibers.c zend_atomic.cpp");
241+
zend_enum.c zend_fibers.c zend_atomic.c");
242242
ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c");
243243

244244
var FIBER_ASSEMBLER = X64 ? PATH_PROG('ML64') : PATH_PROG('ML');

0 commit comments

Comments
 (0)