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