1 /* $Id: memory.h,v 1.5 2010/06/05 19:35:38 fredette Exp $ */
2
3 /* tme/memory.h - header file for memory functions: */
4
5 /*
6 * Copyright (c) 2005 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #ifndef _TME_MEMORY_H
37 #define _TME_MEMORY_H
38
39 #include <tme/common.h>
40 _TME_RCSID("$Id: memory.h,v 1.5 2010/06/05 19:35:38 fredette Exp $");
41
42 /* includes: */
43 #include <tme/threads.h>
44
45 /* macros: */
46
47 /* pointers to memory that may be shared between threads must be
48 qualified with tme_shared, which is normally defined to be
49 volatile, even when threads are cooperative.
50
51 if threads are cooperative and TME_NO_AUDIT_ATOMICS is defined,
52 tme_shared is defined to be empty: */
53 #define tme_shared _tme_volatile
54 #if TME_THREADS_COOPERATIVE
55 #ifdef TME_NO_AUDIT_ATOMICS
56 #undef tme_shared
57 #define tme_shared /**/
58 #endif /* TME_NO_AUDIT_ATOMICS */
59 #endif /* TME_THREADS_COOPERATIVE */
60
61 /* many of the memory macros do pointer casts, and pointer casts can
62 silently discard qualifiers in the original type. to detect this
63 at compile time, we pass a pointer being cast through a function
64 that takes an argument of type void * and only the qualifiers being
65 preserved in the cast: */
66 static _tme_inline void *
_tme_audit_pointer(void * pointer)67 _tme_audit_pointer(void *pointer)
68 {
69 return (pointer);
70 }
71 static _tme_inline tme_shared void *
_tme_audit_pointer_shared(tme_shared void * pointer)72 _tme_audit_pointer_shared(tme_shared void *pointer)
73 {
74 return (pointer);
75 }
76 static _tme_inline _tme_const void *
_tme_audit_pointer_const(_tme_const void * pointer)77 _tme_audit_pointer_const(_tme_const void *pointer)
78 {
79 return (pointer);
80 }
81 #define _tme_cast_pointer(type_cast, type_expected, e) \
82 ((type_cast) (e) + (0 && _tme_audit_pointer(_tme_audit_type(e, type_expected))))
83 #define _tme_cast_pointer_shared(type_cast, type_expected, e) \
84 ((tme_shared type_cast) (e) + (0 && _tme_audit_pointer_shared(_tme_audit_type(e, type_expected))))
85 #define _tme_cast_pointer_const(type_cast, type_expected, e) \
86 ((_tme_const type_cast) (e) + (0 && _tme_audit_pointer_const(_tme_audit_type(e, type_expected))))
87
88 /* NB: by default, we use tme_uint8_t for the atomic flag type and
89 just pass the NULL rwlock pointer to tme_memory_atomic_read8() and
90 tme_memory_atomic_write8(). when threads are not cooperative and
91 the host CPU needs synchronization for atomic reads and writes of
92 tme_uint8_t, the host CPU-specific memory header file must override
93 this default: */
94 #define tme_memory_atomic_flag_t tme_shared tme_uint8_t
95 #define tme_memory_atomic_read_flag(flag) \
96 tme_memory_atomic_read8(flag, (tme_rwlock_t *) 0, sizeof(tme_memory_atomic_flag_t))
97 #define tme_memory_atomic_write_flag(flag, x) \
98 tme_memory_atomic_write8(flag, x, (tme_rwlock_t *) 0, sizeof(tme_memory_atomic_flag_t))
99 #define tme_memory_atomic_init_flag(flag, x) \
100 tme_memory_atomic_write_flag(flag, x)
101
102 /* the default 8-bit memory access macros: */
103 #define tme_memory_read8(mem, align_min) \
104 ((*_tme_cast_pointer_const(tme_uint8_t *, tme_uint8_t *, mem)) + (0 && (align_min)))
105 #define tme_memory_write8(mem, x, align_min) \
106 do { \
107 (*_tme_cast_pointer(tme_uint8_t *, tme_uint8_t *, mem)) \
108 = (x) + (0 && (align_min)); \
109 } while (/* CONSTCOND */ 0)
110 #define tme_memory_atomic_read8(mem, lock, align_min) \
111 ((*_tme_audit_type(mem, tme_uint8_t *)) + (0 && (lock) && (align_min)))
112 #define tme_memory_atomic_write8(mem, x, lock, align_min) \
113 do { \
114 (*_tme_audit_type(mem, tme_uint8_t *)) \
115 = (x) + (0 && (lock) && (align_min)); \
116 } while (/* CONSTCOND */ 0)
117 #define tme_memory_bus_read8(mem, lock, align_min, bus_boundary) \
118 tme_memory_atomic_read8(mem, lock, (align_min) + (0 && (bus_boundary)))
119 #define tme_memory_bus_write8(mem, x, lock, align_min, bus_boundary) \
120 tme_memory_atomic_write8(mem, x, lock, (align_min) + (0 && (bus_boundary)))
121
122 /* include the automatically generated header: */
123 #ifdef _TME_IMPL
124 #include <libtme/memory-auto.h>
125 #else
126 #include <tme/memory-auto.h>
127 #endif
128
129 #if !TME_THREADS_COOPERATIVE
130
131 /* include the host CPU-specific memory header file.
132
133 this header file must define TME_MEMORY_ALIGNMENT_ATOMIC(type),
134 which evaluates to zero if an object of the given type can never be
135 accessed atomically no matter what the alignment, otherwise it
136 evaluates to the minimum alignment needed to guarantee an atomic
137 access.
138
139 this header file must define TME_MEMORY_BUS_BOUNDARY, which is the
140 host CPU's bus boundary. for most CPUs, this will be the size of
141 the largest type for which TME_MEMORY_ALIGNMENT_ATOMIC(type)
142 returns nonzero. if this evaluates to zero, all bus accesses will
143 be emulated as atomic accesses.
144
145 this header file may define TME_MEMORY_ALIGNMENT_ACCEPT(type),
146 which evaluates to the smallest alignment for the given type that
147 is acceptable for partial accesses. this controls when it is
148 cheaper to stop testing an object's alignment and just dereference
149 its parts.
150
151 this header file should also provide as many of the atomic
152 operations as possible, as macros. any operations not provided as
153 macros are performed by the default macro implementations or the
154 function implementations in memory-auto.c. it is also possible for
155 a CPU-specific macro to still call the default function
156 implementation, if, for example, the host CPU can't do an operation
157 atomically at a certain requested alignment.
158
159 if TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) evaluates to
160 zero, all atomic operations will be handled by the default function
161 implementations in memory-auto.c, and those functions will do the
162 atomic operations under software lock. TME_MEMORY_TYPE_COMMON is
163 the dominant type used by all compiled emulated elements.
164
165 if TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) evaluates to
166 nonzero, all atomic operations that can be done atomically by the
167 host CPU, are done atomically (either by host CPU-specific macros
168 or the default macro implementations), and all other atomic
169 operations are handled by the default function implementations,
170 which, instead of doing the atomic operations under software lock,
171 do them with all other threads suspended.
172
173 for best performance, the host CPU-specific memory header file
174 should define TME_MEMORY_ALIGNMENT_ATOMIC to cover as many types as
175 possible, provide the various tme_memory_atomic_cx* operations,
176 and, if the host CPU can do any misaligned atomic reads and writes,
177 the the tme_memory_atomic_read* and tme_memory_atomic_write*
178 operations.
179
180 regardless of TME_MEMORY_ALIGNMENT_ATOMIC(), if reads and writes of
181 properly aligned pointers are not guaranteed to be atomic, this
182 header file must also define tme_memory_atomic_pointer_read() and
183 tme_memory_atomic_pointer_write() as macros. (pointers are handled
184 specially because the size of a pointer may not be a power of
185 two.): */
186
187 #include <tme-memory.h>
188
189 #else /* TME_THREADS_COOPERATIVE */
190
191 /* we don't know if this CPU can do atomic reads and writes at all.
192 it doesn't matter, since threads are cooperative: */
193 #define TME_MEMORY_ALIGNMENT_ATOMIC(type) (0)
194 #define TME_MEMORY_BUS_BOUNDARY (sizeof(tme_uint8_t))
195
196 /* the memory barrier function: */
197 #define TME_MEMORY_BARRIER_READ_BEFORE_READ (0)
198 #define TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE (0)
199 #define TME_MEMORY_BARRIER_READ_BEFORE_WRITE (0)
200 #define TME_MEMORY_BARRIER_WRITE_BEFORE_READ (0)
201 #define tme_memory_barrier(address, size, barrier) do { } while (/* CONSTCOND */ 0 && ((address) + 1) && (size) && (barrier))
202
203 #endif /* TME_THREADS_COOPERATIVE */
204
205 /* if an acceptable-alignment macro is not provided, we assume that it
206 is cheaper to always access an object that may be only
207 half-size-aligned as two half-size-aligned halves, rather than test
208 the object's address for size-alignment and branch: */
209 #ifndef TME_MEMORY_ALIGNMENT_ACCEPT
210 #define TME_MEMORY_ALIGNMENT_ACCEPT(type) (sizeof(type) / 2)
211 #endif /* !defined(TME_MEMORY_ALIGNMENT_ACCEPT) */
212
213 /* if the atomic pointer read and write operations are not provided,
214 there is either no multiprocessing, or normal reads and writes of
215 properly aligned pointers are guaranteed to be atomic: */
216 #ifndef tme_memory_atomic_pointer_read
217 #define tme_memory_atomic_pointer_read(type, lvalue, lock) \
218 (_tme_audit_type(lvalue, type) + (0 && _tme_audit_type(lock, tme_rwlock_t *)))
219 #endif /* !tme_memory_atomic_pointer_read */
220 #ifndef tme_memory_atomic_pointer_write
221 #define tme_memory_atomic_pointer_write(type, lvalue, x, lock) \
222 ((*_tme_audit_type(&(lvalue), type *)) = (x) + (0 && _tme_audit_type(lock, tme_rwlock_t *)))
223 #endif /* !tme_memory_atomic_pointer_write */
224
225 #endif /* !_TME_MEMORY_H */
226