|
|
1.1 root 1: #! /bin/sh
2:
3: # $Id: memory-auto.sh,v 1.1 2006/09/30 12:43:37 fredette Exp $
4:
5: # libtme/memory-auto.sh - automatically generates C code for
6: # memory support:
7:
8: #
9: # Copyright (c) 2005, 2006 Matt Fredette
10: # All rights reserved.
11: #
12: # Redistribution and use in source and binary forms, with or without
13: # modification, are permitted provided that the following conditions
14: # are met:
15: # 1. Redistributions of source code must retain the above copyright
16: # notice, this list of conditions and the following disclaimer.
17: # 2. Redistributions in binary form must reproduce the above copyright
18: # notice, this list of conditions and the following disclaimer in the
19: # documentation and/or other materials provided with the distribution.
20: # 3. All advertising materials mentioning features or use of this software
21: # must display the following acknowledgement:
22: # This product includes software developed by Matt Fredette.
23: # 4. The name of the author may not be used to endorse or promote products
24: # derived from this software without specific prior written permission.
25: #
26: # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27: # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28: # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29: # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30: # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31: # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32: # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34: # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35: # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36: # POSSIBILITY OF SUCH DAMAGE.
37: #
38:
39: header=false
40:
41: for option
42: do
43: case $option in
44: --header) header=true ;;
45: esac
46: done
47:
48: PROG=`basename $0`
49: cat <<EOF
50: /* automatically generated by $PROG, do not edit! */
51:
52: /*
53: * Copyright (c) 2005, 2006 Matt Fredette
54: * All rights reserved.
55: *
56: * Redistribution and use in source and binary forms, with or without
57: * modification, are permitted provided that the following conditions
58: * are met:
59: * 1. Redistributions of source code must retain the above copyright
60: * notice, this list of conditions and the following disclaimer.
61: * 2. Redistributions in binary form must reproduce the above copyright
62: * notice, this list of conditions and the following disclaimer in the
63: * documentation and/or other materials provided with the distribution.
64: * 3. All advertising materials mentioning features or use of this software
65: * must display the following acknowledgement:
66: * This product includes software developed by Matt Fredette.
67: * 4. The name of the author may not be used to endorse or promote products
68: * derived from this software without specific prior written permission.
69: *
70: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
71: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
72: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
73: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
74: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
75: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
76: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
77: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
78: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
79: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
80: * POSSIBILITY OF SUCH DAMAGE.
81: */
82: EOF
83:
84: if $header; then :; else
85: cat <<EOF
86:
87: /* includes: */
88: #include <tme/memory.h>
89:
90: EOF
91: fi
92: cat <<EOF
93:
94: _TME_RCSID("\$Id: memory-auto.sh,v 1.1 2006/09/30 12:43:37 fredette Exp $");
95: EOF
96: if $header; then
97: cat <<EOF
98:
99: /* macros: */
100:
101: /* the plain partial read internal macro: */
102: #define _tme_memory_read(type_whole, type_part, mem, offset) \\
103: (((type_whole) \\
104: *((_tme_const type_part *) \\
105: (_tme_cast_pointer_const(tme_uint8_t *, type_whole *, mem) \\
106: + (offset)))) \\
107: << (8 * (TME_ENDIAN_NATIVE == TME_ENDIAN_BIG \\
108: ? (sizeof(type_whole) \\
109: - ((offset) + sizeof(type_part))) \\
110: : (offset))))
111:
112: /* the plain partial write internal macro: */
113: #define _tme_memory_write(type_whole, type_part, mem, offset, x) \\
114: do { \\
115: *((type_part *) \\
116: (_tme_cast_pointer(tme_uint8_t *, type_whole *, mem) \\
117: + (offset))) \\
118: = (((type_whole) (x)) \\
119: >> (8 * (TME_ENDIAN_NATIVE == TME_ENDIAN_BIG \\
120: ? (sizeof(type_whole) \\
121: - ((offset) + sizeof(type_part))) \\
122: : (offset)))); \\
123: } while (/* CONSTCOND */ 0)
124:
125: /* this tests bits in a memory address: */
126: #define _tme_memory_address_test(mem, bits, align_min) \\
127: (((bits) & ~((align_min - 1))) & ((unsigned long) (mem)))
128:
129: /* this returns a mask of all-bits-one in given type: */
130: #define _tme_memory_type_mask(type, shift) \\
131: ((type) ((((type) 0) - ((type) 1)) shift))
132:
133: EOF
134: fi
135:
136: # the different sizes we handle, and the smallest size that we have to
137: # wrap in an ifdef:
138: #
139: sizes='8 16 32 64'
140: size_ifdef=64
141:
142: # the largest possible host boundary that we will consider:
143: #
144: host_boundary_largest=64
145:
146: # permute for the different sizes:
147: #
148: for size in ${sizes}; do
149:
150: # we don't need bus eight-bit read and write slow functions:
151: #
152: if test ${size} = 8; then continue; fi
153:
154: if test `expr ${size} \>= ${size_ifdef}` = 1; then
155: echo ""
156: echo "#ifdef TME_HAVE_INT${size}_T"
157: fi
158:
159: # emit the bus read and write slow functions:
160: #
161: for op in read write; do
162:
163: # dispatch on the operation:
164: #
165: if test ${op} = read; then
166: op_return_type="tme_uint${size}_t"
167: op_cap=READ
168: op_const="_tme_const "
169: op_proto_operand=
170: op_operand=
171: else
172: op_return_type=void
173: op_cap=WRITE
174: op_const=
175: op_proto_operand=", tme_uint${size}_t"
176: op_operand="${op_proto_operand} x"
177: fi
178:
179: # if we're making the header, just emit a prototype:
180: #
181: if $header; then
182: echo ""
183: echo "/* the bus ${size}-bit ${op} slow function: */"
184: echo "${op_return_type} tme_memory_bus_${op}${size} _TME_P((${op_const}tme_shared tme_uint${size}_t *${op_proto_operand}, tme_rwlock_t *, unsigned int, unsigned int));"
185: continue
186: fi
187:
188: echo ""
189: echo "/* undefine the macro version of tme_memory_bus_${op}${size}: */"
190: echo "#undef tme_memory_bus_${op}${size}"
191: echo ""
192: echo "/* the bus ${size}-bit ${op} slow function: */"
193: echo ${op_return_type}
194: echo "tme_memory_bus_${op}${size}(${op_const}tme_shared tme_uint${size}_t *mem${op_operand}, tme_rwlock_t *rwlock, unsigned int align_min, unsigned int bus_boundary)"
195: echo "{"
196: echo " const unsigned int host_boundary = TME_MEMORY_BUS_BOUNDARY;"
197: echo " unsigned int size_skip;"
198: echo " unsigned int size_done;"
199: if test ${op} = read; then
200: echo " tme_uint${size}_t x;"
201: fi
202:
203: # emit the locals for the possible host boundaries:
204: #
205: host_boundary=${host_boundary_largest}
206: while test ${host_boundary} != 4; do
207: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
208: echo "#ifdef TME_HAVE_INT${host_boundary}_T"
209: fi
210: echo " ${op_const}tme_shared tme_uint${host_boundary}_t *parts${host_boundary};"
211: echo " tme_uint${host_boundary}_t part${host_boundary};"
212: if test ${op} = write; then
213: echo " tme_uint${host_boundary}_t part${host_boundary}_cmp;"
214: fi
215: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
216: echo "#endif /* TME_HAVE_INT${host_boundary}_T */"
217: fi
218: host_boundary=`expr ${host_boundary} / 2`
219: done
220: echo ""
221: echo -n " assert (bus_boundary != 0 && bus_boundary <= host_boundary);"
222:
223: # loop over the possible host boundaries:
224: #
225: host_boundary=${host_boundary_largest}
226: while test ${host_boundary} != 4; do
227:
228: # calculate the worst number of host boundaries that an
229: # access of this size could cross:
230: #
231: host_boundaries_worst=`expr ${size} - 16`
232: host_boundaries_worst=`expr ${host_boundaries_worst} / ${host_boundary}`
233: host_boundaries_worst=`expr ${host_boundaries_worst} + 1`
234:
235: # open this host boundary:
236: #
237: if test ${host_boundary} != 8; then
238: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
239: echo ""
240: echo ""
241: echo "#ifdef TME_HAVE_INT${host_boundary}_T"
242: echo ""
243: echo -n " "
244: fi
245: echo -n " if (host_boundary == sizeof(tme_uint${host_boundary}_t))"
246: fi
247: echo " {"
248:
249: echo ""
250: echo " /* prepare to ${op} the first ${host_boundary}-bit part of the memory: */"
251: echo " parts${host_boundary} = (${op_const}tme_shared tme_uint${host_boundary}_t *) (((unsigned long) mem) & (((unsigned long) 0) - (${host_boundary} / 8)));"
252: echo " size_skip = (((unsigned int) (unsigned long) mem) % (${host_boundary} / 8)) * 8;"
253: echo " size_done = 0;"
254:
255: echo ""
256: echo " /* ${op} the first ${host_boundary}-bit part of the memory: */"
257:
258: # emit two accesses. if the worst number of boundaries
259: # that this access could cross is one, the second access
260: # is inside an if, else it's in a for loop:
261: #
262: indent0=
263: size_done=0
264: size_skip=size_skip
265: access_or=
266: while true; do
267:
268: # read this memory part:
269: #
270: echo "${indent0} part${host_boundary} = tme_memory_atomic_read${host_boundary}(parts${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t));"
271:
272: # if this is a read:
273: #
274: if test ${op} = read; then
275:
276: echo ""
277: echo "${indent0} /* on a little-endian host, we shift off the skip"
278: echo "${indent0} data on the right, and shift the remaining data"
279: echo "${indent0} up into position in the result: */"
280: echo "${indent0} if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE) {"
281: echo "${indent0} x ${access_or}= (((tme_uint${size}_t) (part${host_boundary} >> ${size_skip})) << ${size_done});"
282: echo "${indent0} }"
283: echo ""
284: echo "${indent0} /* on a big-endian host, we shift off the skip data"
285: echo "${indent0} on the left, and shift the remaining data down"
286: echo "${indent0} into position in the result: */"
287: #
288: # NB: on a big-endian host, because the skip data
289: # is on the left, the type of what we shift
290: # depends on how this host boundary size compares
291: # to the access size:
292: #
293: echo "${indent0} else {"
294: echo -n "${indent0} x ${access_or}= "
295: if test `expr ${host_boundary} \> ${size}` = 1; then
296: echo "((part${host_boundary} << ${size_skip}) >> ((${host_boundary} - ${size}) + ${size_done}));"
297: else
298: echo "((((tme_uint${size}_t) part${host_boundary}) << ((${size} - ${host_boundary}) + ${size_skip})) >> ${size_done});"
299: fi
300: echo "${indent0} }"
301:
302: # otherwise, this is a write:
303: #
304: else
305:
306: # start the compare-and-exchange do loop:
307: #
308: echo "${indent0} do {"
309: echo "${indent0} part${host_boundary}_cmp = part${host_boundary};"
310: echo ""
311: echo "${indent0} /* on a little-endian host, we clear with zeroes"
312: echo "${indent0} shifted up past the skip data, and then we"
313: echo "${indent0} insert the data shifted up past the skip data: */"
314: echo "${indent0} if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE) {"
315: echo "${indent0} part${host_boundary} &= (_tme_memory_type_mask(tme_uint${host_boundary}_t, + 0) ^ (((tme_uint${host_boundary}_t) _tme_memory_type_mask(tme_uint${size}_t, << ${size_done})) << ${size_skip}));"
316: echo "${indent0} part${host_boundary} |= (((tme_uint${host_boundary}_t) x) << ${size_skip});"
317: echo "${indent0} }"
318: echo ""
319: echo "${indent0} /* on a big-endian host, we clear with zeroes"
320: echo "${indent0} shifted down past the skip data, and then we"
321: echo "${indent0} insert the data shifted down past the skip data: */"
322: #
323: # NB: on a big-endian host, because the skip data
324: # is on the left, exactly how we shift depends on
325: # how this host boundary size compares to the
326: # access size:
327: #
328: echo "${indent0} else {"
329: if test `expr ${host_boundary} \> ${size}` = 1; then
330: echo "${indent0} part${host_boundary} &= ~((((tme_uint${host_boundary}_t) _tme_memory_type_mask(tme_uint${size}_t, + 0)) << ((${host_boundary} - ${size}) + ${size_done})) >> ${size_skip});"
331: echo "${indent0} part${host_boundary} |= ((((tme_uint${host_boundary}_t) x) << (${host_boundary} - ${size})) >> ${size_skip});"
332: else
333: echo "${indent0} part${host_boundary} &= ~(_tme_memory_type_mask(tme_uint${host_boundary}_t, << ${size_done}) >> ${size_skip});"
334: echo "${indent0} part${host_boundary} |= (x >> ((${size} - ${host_boundary}) + ${size_skip}));"
335: fi
336: echo "${indent0} }"
337: echo ""
338: echo "${indent0} /* loop until we can atomically update this part: */"
339: echo "${indent0} part${host_boundary} = tme_memory_atomic_cx${host_boundary}(parts${host_boundary}, part${host_boundary}_cmp, part${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t));"
340: echo "${indent0} } while (part${host_boundary} != part${host_boundary}_cmp);"
341: if test ${host_boundaries_worst} != 1 || test ${size_skip} != 0; then
342: echo "${indent0} if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE) {"
343: echo "${indent0} x >>= (${host_boundary} - ${size_skip});"
344: echo "${indent0} }"
345: echo "${indent0} else {"
346: echo "${indent0} x <<= (${host_boundary} - ${size_skip});"
347: echo "${indent0} }"
348: fi
349: fi
350:
351: # if this was the first access:
352: #
353: if test ${size_done} = 0; then
354:
355: echo " size_done = ${host_boundary} - size_skip;"
356: size_done=size_done
357: size_skip=0
358: access_or='|'
359:
360: # if the worst number of boundaries that this
361: # access could cross is more than one, we will do
362: # the remaining accesses in a loop, otherwise we
363: # will do one more access, if necessary:
364: #
365: if test ${host_boundaries_worst} = 1; then
366: access_if=true
367: else
368: access_if=false
369:
370: # as an optimization, writes of full parts are
371: # done directly:
372: #
373: if test ${op} = write; then
374: if test `expr ${host_boundary} \< ${size}` = 1; then
375: echo ""
376: if test ${host_boundaries_worst} = 2; then
377: echo " /* try to write one full ${host_boundary}-bit part of memory: */"
378: echo -n " if (__tme_predict_true(size_done <= (${size} - ${host_boundary})))"
379: else
380: echo " /* write as many full ${host_boundary}-bit parts of the memory as we can: */"
381: echo -n " for (; size_done <= (${size} - ${host_boundary}); )"
382: fi
383: echo " {"
384: echo ""
385: echo " /* make a boundary: */"
386: echo " tme_memory_barrier(mem, (${size} / 8), TME_MEMORY_BARRIER_${op_cap}_BEFORE_${op_cap});"
387: echo ""
388: echo " /* write a full ${host_boundary}-bit part of memory: */"
389: echo " part${host_boundary} = (x >> ((TME_ENDIAN_NATIVE == TME_ENDIAN_BIG) * (${size} - ${host_boundary})));"
390: echo " parts${host_boundary}++;"
391: echo " tme_memory_atomic_write${host_boundary}(parts${host_boundary}, part${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t));"
392: echo " size_done += ${host_boundary};"
393: echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE) {"
394: echo " x >>= ${host_boundary};"
395: echo " }"
396: echo " else {"
397: echo " x <<= ${host_boundary};"
398: echo " }"
399: echo " }"
400: access_if=true
401: fi
402: fi
403: fi
404: echo ""
405: if $access_if; then
406: echo " /* ${op} at most one remaining ${host_boundary}-bit part of the memory: */"
407: echo -n " if (__tme_predict_false(size_done < ${size}))"
408: else
409: echo " /* ${op} any remaining ${host_boundary}-bit parts of the memory: */"
410: echo -n " for (; size_done < ${size}; size_done += ${host_boundary})"
411: fi
412: echo " {"
413:
414: echo ""
415: echo " /* make a boundary: */"
416: echo " tme_memory_barrier(mem, (${size} / 8), TME_MEMORY_BARRIER_${op_cap}_BEFORE_${op_cap});"
417: echo ""
418: echo " /* ${op} the next ${host_boundary}-bit part of the memory: */"
419: echo " parts${host_boundary}++;"
420: indent0=" "
421:
422: # otherwise, this was the loop or second access:
423: #
424: else
425: echo " }"
426: break
427: fi
428:
429: done
430:
431: echo " }"
432:
433: # close this host boundary:
434: #
435: if test ${host_boundary} != 8; then
436: echo ""
437: echo -n " else"
438: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
439: echo ""
440: echo ""
441: echo -n "#endif /* TME_HAVE_INT${host_boundary}_T */"
442: if test ${host_boundary} = ${size_ifdef}; then
443: echo ""
444: echo ""
445: echo -n " "
446: fi
447: fi
448: fi
449:
450: # advance:
451: #
452: host_boundary=`expr ${host_boundary} / 2`
453: done
454: if test ${op} = read; then
455: echo ""
456: echo " /* return the value read: */"
457: echo " return (x);"
458: fi
459: echo "}"
460: done
461:
462: if test `expr ${size} \>= ${size_ifdef}` = 1; then
463: echo ""
464: echo "#endif /* TME_HAVE_INT${size}_T */"
465: fi
466:
467: done
468:
469: # emit the bus read and write buffer functions:
470: #
471: for op in read write; do
472:
473: # dispatch on the operation:
474: #
475: if test ${op} = read; then
476: op_cap=READ
477: op_rwlock=rd
478: op_const_mem="_tme_const "
479: op_const_buffer=
480: op_audit_mem=
481: op_audit_buffer=_tme_audit_pointer
482: op_memcpy="(buffer), ((_tme_const tme_uint8_t *) (mem))"
483: op_copy="*buffer = *part_buffer"
484: else
485: op_cap=WRITE
486: op_rwlock=wr
487: op_const_mem=
488: op_const_buffer="_tme_const "
489: op_audit_mem=_tme_audit_pointer_shared
490: op_audit_buffer=_tme_audit_pointer_const
491: op_memcpy="(tme_uint8_t *) (mem), (buffer)"
492: op_copy="*part_buffer = *buffer"
493: fi
494:
495: # if we're making the header:
496: #
497: if $header; then
498:
499: echo ""
500: echo "/* the bus ${op} buffer function and default macro implementation: */"
501:
502: # emit the prototype:
503: #
504: echo "void tme_memory_bus_${op}_buffer _TME_P((${op_const_mem}tme_shared tme_uint8_t *, ${op_const_buffer}tme_uint8_t *, unsigned long, tme_rwlock_t *, unsigned int, unsigned int));"
505:
506: # emit the default macro definition:
507: #
508: echo "#define tme_memory_bus_${op}_buffer(mem, buffer, count, rwlock, align_min, bus_boundary) \\"
509: echo " do { \\"
510: echo " if (TME_THREADS_COOPERATIVE) { \\"
511: echo " memcpy(${op_memcpy}, (count)); \\"
512: echo " } \\"
513: echo " else { \\"
514: echo " tme_memory_bus_${op}_buffer(((${op_const_mem}tme_shared tme_uint8_t *) ${op_audit_mem}(mem)), ((${op_const_buffer}tme_uint8_t *) ${op_audit_buffer}(buffer)), (count), (rwlock), (align_min), (bus_boundary)); \\"
515: echo " } \\"
516: echo " } while (/* CONSTCOND */ 0)"
517:
518: continue
519: fi
520:
521: # start the function:
522: #
523: echo ""
524: echo "/* undefine the macro version of tme_memory_bus_${op}_buffer: */"
525: echo "#undef tme_memory_bus_${op}_buffer"
526: echo ""
527: echo "/* the bus ${op} buffer function: */"
528: echo "void"
529: echo "tme_memory_bus_${op}_buffer(${op_const_mem}tme_shared tme_uint8_t *mem, ${op_const_buffer}tme_uint8_t *buffer, unsigned long count, tme_rwlock_t *rwlock, unsigned int align_min, unsigned int bus_boundary)"
530: echo "{"
531: echo " const unsigned int host_boundary = TME_MEMORY_BUS_BOUNDARY;"
532: echo " ${op_const_mem}tme_uint8_t *part_buffer;"
533: echo " unsigned int count_done;"
534: echo " unsigned int count_misaligned;"
535: echo " unsigned int bits_misaligned;"
536:
537: # emit the locals for the possible host boundaries:
538: #
539: host_boundary=${host_boundary_largest}
540: while test ${host_boundary} != 4; do
541: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
542: echo "#ifdef TME_HAVE_INT${host_boundary}_T"
543: fi
544: echo " ${op_const}tme_shared tme_uint${host_boundary}_t *parts${host_boundary};"
545: echo " tme_uint${host_boundary}_t part${host_boundary}_buffer;"
546: echo " tme_uint${host_boundary}_t part${host_boundary};"
547: echo " tme_uint${host_boundary}_t part${host_boundary}_next;"
548: if test ${op} = write; then
549: echo " tme_uint${host_boundary}_t part${host_boundary}_mask;"
550: echo " tme_uint${host_boundary}_t part${host_boundary}_cmp;"
551: fi
552: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
553: echo "#endif /* TME_HAVE_INT${host_boundary}_T */"
554: fi
555: host_boundary=`expr ${host_boundary} / 2`
556: done
557: echo ""
558: echo " assert (count != 0);"
559: echo " assert (bus_boundary != 0);"
560:
561: echo ""
562: echo " /* if we are locking for all memory accesses, lock memory"
563: echo " around a memcpy: */"
564: echo " if (TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) == 0) {"
565: echo " tme_rwlock_${op_rwlock}lock(rwlock);"
566: echo " memcpy(${op_memcpy}, (count));"
567: echo " tme_rwlock_unlock(rwlock);"
568: echo " }"
569: echo ""
570: echo " /* otherwise, if the emulated bus boundary is greater than the"
571: echo " host's bus boundary, we are forced to stop all other threads"
572: echo " around a memcpy: */"
573: echo " else if (__tme_predict_false(bus_boundary == 0"
574: echo " || bus_boundary > host_boundary)) {"
575: echo " tme_thread_suspend_others();"
576: echo " memcpy(${op_memcpy}, (count) + (0 && align_min));"
577: echo " tme_thread_resume_others();"
578: echo " }"
579:
580: # loop over the possible host boundaries:
581: #
582: host_boundary=${host_boundary_largest}
583: indent0=
584: while test ${host_boundary} != 4; do
585:
586: # open this host boundary:
587: #
588: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
589: echo ""
590: echo "#ifdef TME_HAVE_INT${host_boundary}_T"
591: fi
592: echo ""
593: echo -n " else"
594: if test ${host_boundary} != 8; then
595: echo -n " if (host_boundary == sizeof(tme_uint${host_boundary}_t))"
596: fi
597: echo " {"
598:
599: if test ${op} = write; then
600: op_part_read="tme_memory_read${host_boundary}((const tme_uint${host_boundary}_t *) buffer, sizeof(tme_uint${host_boundary}_t))"
601: op_part_write="tme_memory_atomic_write${host_boundary}(parts${host_boundary}, part${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t))"
602: else
603: op_part_read="tme_memory_atomic_read${host_boundary}(parts${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t))"
604: op_part_write="tme_memory_write${host_boundary}((tme_uint${host_boundary}_t *) buffer, part${host_boundary}, sizeof(tme_uint${host_boundary}_t))"
605: fi
606:
607: echo ""
608: echo " /* make a ${host_boundary}-bit pointer to the memory: */"
609: echo " parts${host_boundary} = (${op_const}tme_shared tme_uint${host_boundary}_t *) mem;"
610: echo ""
611: echo " /* if this pointer is not ${host_boundary}-bit aligned: */"
612: echo " if (__tme_predict_false((((unsigned long) parts${host_boundary}) % sizeof(tme_uint${host_boundary}_t)) != 0)) {"
613: echo ""
614: echo " /* get the misalignment from the previous ${host_boundary}-bit boundary: */"
615: echo " count_misaligned = ((unsigned long) parts${host_boundary}) % sizeof(tme_uint${host_boundary}_t);"
616: echo ""
617: echo " /* truncate this pointer to the previous ${host_boundary}-bit boundary: */"
618: echo " parts${host_boundary} = (${op_const}tme_shared tme_uint${host_boundary}_t *) (((unsigned long) parts${host_boundary}) & (((unsigned long) 0) - sizeof(tme_uint${host_boundary}_t)));"
619: echo ""
620: echo " /* get the number of bytes to ${op} in the first ${host_boundary}-bit memory part: */"
621: echo " count_done = sizeof(tme_uint${host_boundary}_t) - count_misaligned;"
622: echo " if (__tme_predict_false(count_done > count)) {"
623: echo " count_done = count;"
624: echo " }"
625:
626: if test ${op} = write; then
627: echo ""
628: echo " /* make a mask that clears for the data to write in the"
629: echo " first ${host_boundary}-bit memory part: */"
630: echo " part${host_boundary}_mask = 1;"
631: echo " part${host_boundary}_mask = (part${host_boundary}_mask << (count_done * 8)) - 1;"
632: echo " part${host_boundary}_mask"
633: echo " <<= (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE"
634: echo " ? (count_misaligned * 8)"
635: echo " : (${host_boundary} - ((count_misaligned + count_done) * 8)));"
636: echo " part${host_boundary}_mask = ~part${host_boundary}_mask;"
637: echo ""
638: echo " /* copy from the buffer the bytes to write in the first"
639: echo " ${host_boundary}-bit memory part: */"
640: echo " part${host_boundary}_buffer = 0;"
641: else
642: echo ""
643: echo " /* read the first ${host_boundary}-bit memory part: */"
644: echo " part${host_boundary}_buffer = ${op_part_read};"
645: echo " parts${host_boundary}++;"
646: echo ""
647: echo " /* copy to the buffer the bytes to read in the first"
648: echo " ${host_boundary}-bit memory part: */"
649: fi
650: echo " part_buffer = ((tme_uint8_t *) &part${host_boundary}_buffer) + count_misaligned;"
651: echo " count -= count_done;"
652: echo " do {"
653: echo " ${op_copy};"
654: echo " part_buffer++;"
655: echo " buffer++;"
656: echo " } while (--count_done != 0);"
657:
658: if test ${op} = write; then
659: echo ""
660: echo " /* compare-and-exchange the first ${host_boundary}-bit memory part: */"
661: echo " part${host_boundary} = ${op_part_read};"
662: echo " do {"
663: echo " part${host_boundary}_cmp = part${host_boundary};"
664: echo " part${host_boundary} = (part${host_boundary} & part${host_boundary}_mask) | part${host_boundary}_buffer;"
665: echo " part${host_boundary} = tme_memory_atomic_cx${host_boundary}(parts${host_boundary}, part${host_boundary}_cmp, part${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t));"
666: echo " } while (part${host_boundary} != part${host_boundary}_cmp);"
667: echo " parts${host_boundary}++;"
668: fi
669: echo " }"
670:
671: echo ""
672: echo " /* if we have full ${host_boundary}-bit parts to ${op}: */"
673: echo " if (__tme_predict_true(count >= sizeof(tme_uint${host_boundary}_t))) {"
674: echo ""
675: echo " /* if the buffer is ${host_boundary}-bit aligned: */"
676: echo " if (__tme_predict_true((((unsigned long) buffer) % sizeof(tme_uint${host_boundary}_t)) == 0)) {"
677: echo ""
678: echo " /* ${op} full ${host_boundary}-bit parts without shifting: */"
679: echo " do {"
680: echo " part${host_boundary} = ${op_part_read};"
681: echo " ${op_part_write};"
682: echo ""
683: echo " /* advance: */"
684: echo " parts${host_boundary}++;"
685: echo " buffer += sizeof(tme_uint${host_boundary}_t);"
686: echo " count -= sizeof(tme_uint${host_boundary}_t);"
687: echo " } while (count >= sizeof(tme_uint${host_boundary}_t));"
688: echo " }"
689: echo ""
690: echo " /* otherwise, the buffer is not ${host_boundary}-bit aligned: */"
691: echo " else {"
692: echo ""
693: echo " /* get the misalignment to the next ${host_boundary}-bit boundary: */"
694: echo " count_misaligned = (sizeof(tme_uint${host_boundary}_t) - ((unsigned int) (unsigned long) buffer)) % sizeof(tme_uint${host_boundary}_t);"
695: if test ${op} = write; then
696: echo ""
697: echo " /* copy from the buffer until it is aligned: */"
698: echo " part${host_boundary}_buffer = 0;"
699: else
700: echo ""
701: echo " /* read the next ${host_boundary}-bit memory part: */"
702: echo " part${host_boundary}_buffer = ${op_part_read};"
703: echo " parts${host_boundary}++;"
704: echo ""
705: echo " /* copy to the buffer until it is aligned: */"
706: fi
707: echo " part_buffer = ((${op_const_mem}tme_uint8_t *) &part${host_boundary}_buffer);"
708: echo " count_done = count_misaligned;"
709: echo " count -= count_misaligned;"
710: echo " do {"
711: echo " ${op_copy};"
712: echo " part_buffer++;"
713: echo " buffer++;"
714: echo " } while (--count_done != 0);"
715:
716: echo ""
717: echo " /* ${op} full ${host_boundary}-bit words with shifting: */"
718: echo " bits_misaligned = count_misaligned * 8;"
719: if test ${op} = write; then
720: op_shift=bits_misaligned
721: op_shift_next="(${host_boundary} - bits_misaligned)"
722: echo " part${host_boundary} = part${host_boundary}_buffer;"
723: else
724: op_shift="(${host_boundary} - bits_misaligned)"
725: op_shift_next=bits_misaligned
726: echo " part${host_boundary}"
727: echo " = (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE"
728: echo " ? (part${host_boundary}_buffer >> ${op_shift_next})"
729: echo " : (part${host_boundary}_buffer << ${op_shift_next}));"
730: fi
731: echo " for (; count >= sizeof(tme_uint${host_boundary}_t); ) {"
732: echo " part${host_boundary}_next = ${op_part_read};"
733: echo " if (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE) {"
734: echo " part${host_boundary} |= (part${host_boundary}_next << ${op_shift});"
735: echo " ${op_part_write};"
736: echo " part${host_boundary} = (part${host_boundary}_next >> ${op_shift_next});"
737: echo " }"
738: echo " else {"
739: echo " part${host_boundary} |= (part${host_boundary}_next >> ${op_shift});"
740: echo " ${op_part_write};"
741: echo " part${host_boundary} = (part${host_boundary}_next << ${op_shift_next});"
742: echo " }"
743: echo ""
744: echo " /* advance: */"
745: echo " parts${host_boundary}++;"
746: echo " buffer += sizeof(tme_uint${host_boundary}_t);"
747: echo " count -= sizeof(tme_uint${host_boundary}_t);"
748: echo " }"
749:
750: echo ""
751: echo " /* calculate how many more bytes there are to ${op} in this"
752: echo " ${host_boundary}-bit memory part: */"
753: echo " count_done = sizeof(tme_uint${host_boundary}_t) - count_misaligned;"
754: echo " part${host_boundary}_buffer = part${host_boundary};"
755: if test ${op} = write; then
756: echo ""
757: echo " /* if we can't write one more full ${host_boundary}-bit memory part: */"
758: echo " if (count_done > count) {"
759: echo ""
760: echo " /* we will reread this data to write below: */"
761: echo " buffer -= count_misaligned;"
762: echo " count += count_misaligned;"
763: echo " }"
764: echo ""
765: echo " /* otherwise, we can write one more full ${host_boundary}-bit memory part: */"
766: echo " else {"
767: echo ""
768: echo " /* copy from the buffer until we have the full ${host_boundary}-bit part: */"
769: echo " part_buffer = ((${op_const_mem}tme_uint8_t *) &part${host_boundary}_buffer) + count_misaligned;"
770: echo " count -= count_done;"
771: echo " do {"
772: echo " ${op_copy};"
773: echo " part_buffer++;"
774: echo " buffer++;"
775: echo " } while (--count_done != 0);"
776: echo ""
777: echo " /* write the last full ${host_boundary}-bit memory part: */"
778: echo " part${host_boundary} = part${host_boundary}_buffer;"
779: echo " ${op_part_write};"
780: echo " }"
781: else
782: echo ""
783: echo " /* copy to the buffer the remaining bytes in this ${host_boundary}-bit part: */"
784: echo " if (count_done > count) {"
785: echo " count_done = count;"
786: echo " }"
787: echo " part_buffer = ((${op_const_mem}tme_uint8_t *) &part${host_boundary}_buffer);"
788: echo " count -= count_done;"
789: echo " do {"
790: echo " ${op_copy};"
791: echo " part_buffer++;"
792: echo " buffer++;"
793: echo " } while (--count_done != 0);"
794: fi
795: echo " }"
796: echo " }"
797:
798: echo ""
799: echo " /* if we still have bytes to ${op}: */"
800: echo " if (__tme_predict_false(count > 0)) {"
801: echo ""
802: echo " /* we must have less than a full ${host_boundary}-bit part to ${op}: */"
803: echo " assert (count < sizeof(tme_uint${host_boundary}_t));"
804: if test ${op} = write; then
805: echo ""
806: echo " /* make a mask that clears for the data to write in the last"
807: echo " ${host_boundary}-bit memory part: */"
808: echo " part${host_boundary}_mask"
809: echo " = (TME_ENDIAN_NATIVE == TME_ENDIAN_LITTLE"
810: echo " ? _tme_memory_type_mask(tme_uint${host_boundary}_t, << (count * 8))"
811: echo " : _tme_memory_type_mask(tme_uint${host_boundary}_t, >> (count * 8)));"
812: echo ""
813: echo " /* copy from the buffer the bytes to write in the last"
814: echo " ${host_boundary}-bit memory part: */"
815: echo " part${host_boundary}_buffer = 0;"
816: else
817: echo ""
818: echo " /* read the last ${host_boundary}-bit memory part: */"
819: echo " part${host_boundary}_buffer = ${op_part_read};"
820: echo ""
821: echo " /* copy to the buffer the bytes to read in the first"
822: echo " ${host_boundary}-bit memory part: */"
823: fi
824: echo " part_buffer = ((${op_const_mem}tme_uint8_t *) &part${host_boundary}_buffer);"
825: echo " count_done = count;"
826: echo " do {"
827: echo " ${op_copy};"
828: echo " part_buffer++;"
829: echo " buffer++;"
830: echo " } while (--count_done != 0);"
831: if test ${op} = write; then
832: echo ""
833: echo " /* compare-and-exchange the last ${host_boundary}-bit memory part: */"
834: echo " part${host_boundary} = ${op_part_read};"
835: echo " do {"
836: echo " part${host_boundary}_cmp = part${host_boundary};"
837: echo " part${host_boundary} = (part${host_boundary} & part${host_boundary}_mask) | part${host_boundary}_buffer;"
838: echo " part${host_boundary} = tme_memory_atomic_cx${host_boundary}(parts${host_boundary}, part${host_boundary}_cmp, part${host_boundary}, rwlock, sizeof(tme_uint${host_boundary}_t));"
839: echo " } while (part${host_boundary} != part${host_boundary}_cmp);"
840: fi
841: echo " }"
842:
843: # close this host boundary:
844: #
845: echo ""
846: echo " }"
847: if test `expr ${host_boundary} \>= ${size_ifdef}` = 1; then
848: echo ""
849: echo "#endif /* TME_HAVE_INT${host_boundary}_T */"
850: fi
851:
852: # advance:
853: #
854: host_boundary=`expr ${host_boundary} / 2`
855: done
856:
857: echo "}"
858: done
859:
860: # permute for the different sizes:
861: #
862: for size in ${sizes}; do
863:
864: if test `expr ${size} \>= ${size_ifdef}` = 1; then
865: echo ""
866: echo "#ifdef TME_HAVE_INT${size}_T"
867: fi
868:
869: # permute for the different types of memory read and write macros.
870: # all of these macros work with memory of any alignment, but for
871: # best performance they accept the minimum alignment of the memory
872: # known at compile time.
873: #
874: # the "plain" macros are used to read and write memory that is not
875: # shared.
876: #
877: # the "atomic" macros are used to read and write memory that is
878: # shared. the total read or write is always atomic.
879: #
880: # the "bus" macros are used to read and write memory that is
881: # shared, with the access split across the given bus boundary into
882: # atomic partials.
883: #
884: for type_user in plain atomic bus; do
885:
886: # permute over read and write:
887: #
888: for op in read write; do
889:
890: # these macros are for the header file, and there are
891: # hand-coded macros for 8-bit values:
892: #
893: if $header; then :; else continue; fi
894: if test ${size} = 8; then continue; fi
895:
896: # characterize this macro:
897: #
898: type="_${type_user}"
899: type_lock=", lock"
900: type_part=_atomic
901: type_bus_boundary=
902: case ${type_user} in
903: plain)
904: type=
905: type_lock=
906: type_part=
907: ;;
908: bus)
909: type_bus_boundary=", bus_boundary"
910: ;;
911: esac
912: if test ${op} = read; then
913: op_x=
914: op_open=" ( \\"
915: op_indent0=" "
916: op_indent1=${op_indent0}
917: op_then="${op_indent1}? \\"
918: op_else_if="${op_indent1}: \\"
919: op_else=${op_else_if}
920: op_indent2="${op_indent1} "
921: op_semi=
922: op_close=" )"
923: else
924: op_x=", x"
925: op_open=" do { \\@ if \\"
926: op_indent0=" "
927: op_indent1=${op_indent0}
928: op_then=" { \\"
929: op_else_if=" } \\@ else if \\"
930: op_else=" } \\@ else \\@${op_then}"
931: op_indent2=" "
932: op_semi=';'
933: op_close=" } \\@ } while (/* CONSTCOND */ 0)"
934: fi
935:
936: # start the macro:
937: #
938: echo ""
939: echo "/* the default ${size}-bit memory ${type_user} ${op} macro: */"
940: echo "#define tme_memory${type}_${op}${size}(mem${op_x}${type_lock}, align_min${type_bus_boundary}) \\"
941: echo "${op_open}" | tr '@' '\n'
942:
943: # dispatch on the macro type:
944: #
945: case ${type_user} in
946:
947: plain)
948: echo "${op_indent0}/* if we know at compile time that the memory is aligned \\"
949: echo "${op_indent0} enough to ${op} directly, do the single direct ${op}. \\"
950: echo "${op_indent0}\\"
951: echo "${op_indent0} otherwise, if we know at compile time that the memory \\"
952: echo "${op_indent0} is less aligned than the smallest acceptable parts size, \\"
953: echo "${op_indent0} test if the memory is aligned enough to ${op} directly, \\"
954: echo "${op_indent0} and do the single direct ${op} if it is: */ \\"
955: echo "${op_indent0}(__tme_predict_true((_TME_ALIGNOF_INT${size}_T == 1 \\"
956: echo "${op_indent0} || (align_min) >= _TME_ALIGNOF_INT${size}_T) \\"
957: echo "${op_indent0} || ((align_min) < TME_MEMORY_ALIGNMENT_ACCEPT(tme_uint${size}_t) \\"
958: echo "${op_indent0} && _tme_memory_address_test(mem, _TME_ALIGNOF_INT${size}_T - 1, align_min) == 0))) \\"
959: echo "${op_then}"
960: echo "${op_indent2}_tme_memory_${op}(tme_uint${size}_t, tme_uint${size}_t, mem, 0${op_x})${op_semi} \\"
961:
962: # loop over the possible part sizes:
963: #
964: size_part=8
965: while test ${size_part} != ${size}; do
966:
967: # at this point, we know that the memory is at
968: # least aligned to the part size:
969: #
970:
971: # if this is the last possible part size:
972: #
973: if test `expr ${size_part} \* 2` = ${size}; then
974:
975: # we will just access all parts of this size:
976: #
977: echo "${op_else}" | tr '@' '\n'
978:
979: # otherwise, this is not the last possible part
980: # size:
981: #
982: else
983:
984: # if we know at compile time that accessing
985: # all parts of this size is acceptable over
986: # further testing of the address, and the
987: # memory is not more aligned than this part
988: # size, we will just access all parts of this
989: # size:
990: #
991: echo "${op_else_if}" | tr '@' '\n'
992: echo "${op_indent1}((TME_MEMORY_ALIGNMENT_ACCEPT(tme_uint${size}_t) <= sizeof(tme_uint${size_part}_t)) \\"
993: echo "${op_indent1} && ((align_min) <= sizeof(tme_uint${size_part}_t))) \\"
994: echo "${op_then}"
995: fi
996:
997: # we always emit one set of partial accesses, all
998: # of this part size.
999: #
1000: # then, if this isn't the last possible part size,
1001: # we test if the address is aligned only to this
1002: # part size. if it is, we emit a second set of partial
1003: # accesses, to transfer this part size, some number
1004: # of parts of the next part size, and one part of this
1005: # part size:
1006: #
1007: size_now=${size_part}
1008: misaligned=false
1009: while true; do
1010: size_done=0
1011: while test ${size_done} != ${size}; do
1012:
1013: # emit one partial transfer:
1014: #
1015: echo -n "${op_indent2}"
1016: op_delim=${op_semi}
1017: if test ${op} = read; then
1018: if test ${size_done} = 0; then echo -n '('; else echo -n ' | '; fi
1019: if test `expr ${size_done} + ${size_now}` = ${size}; then op_delim=')'; fi
1020: fi
1021: echo "_tme_memory${type}_${op}(tme_uint${size}_t, tme_uint${size_now}_t, mem, (${size_done} / 8)${op_x})${op_delim} \\"
1022:
1023: # advance:
1024: #
1025: size_done=`expr ${size_done} + ${size_now}`
1026: size_now=`expr ${size} - ${size_done}`
1027: if test `expr ${size_now} \> ${size_part}` = 1; then
1028: size_now=${size_part}
1029: fi
1030: done
1031:
1032: # if we have already done the misaligned set,
1033: # stop now:
1034: #
1035: if ${misaligned}; then
1036: break
1037: fi
1038: misaligned=true
1039:
1040: # advance to test and do the misaligned set:
1041: #
1042: size_now=${size_part}
1043: size_part=`expr ${size_part} \* 2`
1044: if test ${size_part} = ${size}; then
1045: break
1046: fi
1047: echo "${op_else_if}" | tr '@' '\n'
1048: echo "${op_indent1}(_tme_memory_address_test(mem, sizeof(tme_uint${size_now}_t), align_min) != 0) \\"
1049: echo "${op_then}"
1050: done
1051: done
1052: ;;
1053:
1054: atomic)
1055: echo "${op_indent0}/* if we aren't locking for all memory accesses, and we can \\"
1056: echo "${op_indent0} make direct ${size}-bit accesses, and this memory is aligned \\"
1057: echo "${op_indent0} enough to make a single direct atomic access, do the single \\"
1058: echo "${op_indent0} direct atomic ${op}: */ \\"
1059: echo "${op_indent0}(__tme_predict_true(TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) != 0 \\"
1060: echo "${op_indent0} && TME_MEMORY_ALIGNMENT_ATOMIC(tme_uint${size}_t) != 0 \\"
1061: echo "${op_indent0} && _tme_memory_address_test(mem, TME_MEMORY_ALIGNMENT_ATOMIC(tme_uint${size}_t) - 1, align_min) == 0)) \\"
1062: echo "${op_then}"
1063: echo "${op_indent2}(*_tme_audit_type(mem, tme_uint${size}_t *)) \\"
1064: if test ${op} = write; then
1065: echo "${op_indent2} = (x); \\"
1066: fi
1067: echo "${op_indent1}/* otherwise, we must do a slow indirect atomic ${op}: */ \\"
1068: echo "${op_else}" | tr '@' '\n'
1069: echo "${op_indent2}tme_memory${type}_${op}${size}(mem${op_x}${type_lock}, align_min)${op_semi} \\"
1070: ;;
1071:
1072: bus)
1073: echo "${op_indent0}/* if threads are cooperative, do a plain ${op}: */ \\"
1074: echo "${op_indent0}(TME_THREADS_COOPERATIVE) \\"
1075: echo "${op_then}"
1076: echo -n "${op_indent2}tme_memory_${op}${size}("
1077: # this strips off the tme_shared qualifier:
1078: #
1079: if test ${op} = read; then
1080: echo -n "(_tme_const tme_uint${size}_t *) _tme_audit_type(mem, tme_uint${size}_t *)"
1081: else
1082: echo -n "(tme_uint${size}_t *) _tme_cast_pointer_shared(tme_uint${size}_t *, tme_uint${size}_t *, mem)"
1083: fi
1084: echo "${op_x}, align_min)${op_semi} \\"
1085:
1086: echo "${op_indent1}/* otherwise, if we aren't locking for all memory accesses, the \\"
1087: echo "${op_indent1} host supports misaligned ${size}-bit accesses, the host's bus \\"
1088: echo "${op_indent1} boundary is greater than or equal to the emulated bus \\"
1089: echo "${op_indent1} boundary, and this memory is aligned enough, do a single \\"
1090: echo "${op_indent1} direct bus ${op}: */ \\"
1091: echo "${op_else_if}" | tr '@' '\n'
1092: echo "${op_indent1}(__tme_predict_true(TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) != 0 \\"
1093: echo "${op_indent1} && _TME_ALIGNOF_INT${size}_T < sizeof(tme_uint${size}_t) \\"
1094: echo "${op_indent1} && TME_MEMORY_BUS_BOUNDARY >= (bus_boundary) \\"
1095: echo "${op_indent1} && _tme_memory_address_test(mem, _TME_ALIGNOF_INT${size}_T - 1, align_min) == 0)) \\"
1096: echo "${op_then}"
1097: echo "${op_indent2}(*_tme_audit_type(mem, tme_uint${size}_t *)) \\"
1098: if test ${op} = write; then
1099: echo "${op_indent2} = (x); \\"
1100: fi
1101:
1102: echo "${op_indent1}/* otherwise, if we're locking for all memory accesses, or \\"
1103: echo "${op_indent1} if this memory must cross at least one host bus boundary \\"
1104: echo "${op_indent1} and the host bus boundary is less than the emulated bus \\"
1105: echo "${op_indent1} boundary, do a slow indirect atomic ${op}: */ \\"
1106: echo "${op_else_if}" | tr '@' '\n'
1107: echo "${op_indent1}(__tme_predict_false(TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) == 0 \\"
1108: echo "${op_indent1} || (sizeof(tme_uint${size}_t) > TME_MEMORY_BUS_BOUNDARY \\"
1109: echo "${op_indent1} && TME_MEMORY_BUS_BOUNDARY < (bus_boundary)))) \\"
1110: echo "${op_then}"
1111: echo "${op_indent2}tme_memory_atomic_${op}${size}(mem${op_x}, lock, align_min)${op_semi} \\"
1112:
1113: echo "${op_indent1}/* otherwise, if the memory is not larger than the emulated \\"
1114: echo "${op_indent1} bus boundary, or if size-alignment would mean an atomic \\"
1115: echo "${op_indent1} host access and it is size-aligned, do a single atomic \\"
1116: echo "${op_indent1} ${op}, which may be direct or slow: */ \\"
1117: echo "${op_else_if}" | tr '@' '\n'
1118: echo "${op_indent1}(__tme_predict_true((sizeof(tme_uint${size}_t) <= (bus_boundary) \\"
1119: echo "${op_indent1} || (TME_MEMORY_ALIGNMENT_ATOMIC(tme_uint${size}_t) != 0 \\"
1120: echo "${op_indent1} && TME_MEMORY_ALIGNMENT_ATOMIC(tme_uint${size}_t) <= sizeof(tme_uint${size}_t))) \\"
1121: echo "${op_indent1} && _tme_memory_address_test(mem, sizeof(tme_uint${size}_t) - 1, align_min) == 0)) \\"
1122: echo "${op_then}"
1123: echo "${op_indent2}tme_memory_atomic_${op}${size}(mem${op_x}, lock, sizeof(tme_uint${size}_t))${op_semi} \\"
1124: echo "${op_indent1}/* otherwise, we must do a slow bus ${op}: */ \\"
1125: echo "${op_else}" | tr '@' '\n'
1126: echo "${op_indent2}tme_memory${type}_${op}${size}(mem${op_x}${type_lock}, align_min, bus_boundary)${op_semi} \\"
1127: ;;
1128:
1129: esac
1130:
1131: # close this macro:
1132: #
1133: echo "${op_close}" | tr '@' '\n'
1134: done
1135: done
1136:
1137: echo ""
1138: echo "/* the ${size}-bit atomic operations: */"
1139:
1140: # the atomic operations. NB that cx, read, and write are
1141: # deliberately at the end and in that order, to allow all earlier
1142: # default implementations to still use any host CPU-specific cx,
1143: # read and write macros:
1144: #
1145: for op in add sub mul div and or xor not neg xchg cx read write; do
1146:
1147: # dispatch on the operation. NB that we don't need to
1148: # generate 8-bit atomic read and write operations:
1149: #
1150: op_rval="tme_uint${size}_t"
1151: op_const=
1152: op_proto_operand=", tme_uint${size}_t"
1153: op_operand=operand
1154: op_from=to
1155: op_operator=
1156: op_operation=
1157: op_indent=
1158: case ${op} in
1159: read)
1160: if test ${size} = 8; then continue; fi
1161: op_const="_tme_const "
1162: op_from=from
1163: op_proto_operand=
1164: ;;
1165: write)
1166: if test ${size} = 8; then continue; fi
1167: op_rval=void
1168: op_operand=value_written
1169: ;;
1170: cx)
1171: op_proto_operand=", tme_uint${size}_t, tme_uint${size}_t"
1172: op_operand=value_written
1173: op_indent=" "
1174: ;;
1175: add) op_operator='+' ;;
1176: sub) op_operator='-' ;;
1177: mul) op_operator='*' ;;
1178: div) op_operator='/' ;;
1179: and) op_operator='&' ;;
1180: or) op_operator='|' ;;
1181: xor) op_operator='^' ;;
1182: not) op_operation='~value_read' ; op_proto_operand= ;;
1183: neg) op_operation='0 - value_read' ; op_proto_operand= ;;
1184: xchg) op_operand=value_written
1185: esac
1186: if test "x${op_operator}" != x; then
1187: op_operation="value_read ${op_operator} ${op_operand}"
1188: fi
1189:
1190: # if we're making the header, just emit a prototype:
1191: #
1192: if $header; then
1193: echo "${op_rval} tme_memory_atomic_${op}${size} _TME_P((${op_const}tme_shared tme_uint${size}_t *${op_proto_operand}, tme_rwlock_t *, unsigned int));"
1194: continue
1195: fi
1196:
1197: echo ""
1198: echo "/* undefine any macro version of tme_memory_atomic_${op}${size}: */"
1199: echo "#undef tme_memory_atomic_${op}${size}"
1200: echo ""
1201: echo "/* the ${size}-bit atomic ${op} function: */"
1202: echo "${op_rval}"
1203: echo "tme_memory_atomic_${op}${size}(${op_const}tme_shared tme_uint${size}_t *memory,"
1204: if test ${op} = cx; then
1205: echo " tme_uint${size}_t value_cmp,"
1206: fi
1207: if test "x${op_proto_operand}" != x; then
1208: echo " tme_uint${size}_t ${op_operand},"
1209: fi
1210: echo " tme_rwlock_t *rwlock,"
1211: echo " unsigned int align_min)"
1212: echo "{"
1213: if test ${op} != write; then
1214: echo " tme_uint${size}_t value_read;"
1215: fi
1216: if test "x${op_operation}" != x; then
1217: echo " tme_uint${size}_t value_written;"
1218: fi
1219: if test ${op} = read || test ${op} = write || test ${op} = cx; then :; else
1220: echo " tme_uint${size}_t value_read_verify;"
1221: fi
1222: echo ""
1223: echo " /* if we can't make direct accesses at all, all atomic"
1224: echo " accesses must be done under lock. (when threads are"
1225: echo " cooperative the actual locking isn't needed): */"
1226: echo " if (TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) == 0) {"
1227: echo " if (!TME_THREADS_COOPERATIVE) {"
1228: if test ${op} = read; then
1229: echo " tme_rwlock_rdlock(rwlock);"
1230: else
1231: echo " tme_rwlock_wrlock(rwlock);"
1232: fi
1233: echo " }"
1234: if test ${op} != write; then
1235: echo " value_read = tme_memory_read${size}((_tme_const tme_uint${size}_t *) memory, align_min);"
1236: fi
1237: if test ${op} = cx; then
1238: echo " if (value_read == value_cmp) {"
1239: fi
1240: if test "x${op_operation}" != x; then
1241: echo " value_written = ${op_operation};"
1242: fi
1243: if test ${op} != read; then
1244: echo "${op_indent} tme_memory_write${size}((tme_uint${size}_t *) memory, value_written, align_min);"
1245: fi
1246: if test "x${op_indent}" != x; then
1247: echo " }"
1248: fi
1249: echo " if (!TME_THREADS_COOPERATIVE) {"
1250: echo " tme_rwlock_unlock(rwlock);"
1251: echo " }"
1252: echo " }"
1253: echo ""
1254: echo " /* otherwise, threads are not cooperative and this host CPU"
1255: echo " can make atomic accesses to at least the most common memory"
1256: echo " size."
1257: echo ""
1258: echo " in that case, the only reason this function should get"
1259: echo " called is if the host CPU can't do an atomic ${size}-bit"
1260: echo " ${op} at all, or if it can't do it at this alignment."
1261: if test ${op} = read || test ${op} = write || test ${op} = cx; then
1262: echo ""
1263: echo " we assume that these problematic atomic ${op}s are rare,"
1264: echo " and to emulate them we simply stop all other threads while"
1265: echo " doing the ${op}: */"
1266: echo " else {"
1267: echo " tme_thread_suspend_others();"
1268: if test ${op} != write; then
1269: echo " value_read = tme_memory_read${size}((_tme_const tme_uint${size}_t *) memory, align_min);"
1270: fi
1271: if test ${op} = cx; then
1272: echo " if (value_read == value_cmp) {"
1273: fi
1274: if test ${op} != read; then
1275: echo "${op_indent} tme_memory_write${size}((tme_uint${size}_t *) memory, value_written, align_min);"
1276: fi
1277: if test "x${op_indent}" != x; then
1278: echo " }"
1279: fi
1280: echo " tme_thread_resume_others();"
1281: echo " }"
1282: else
1283: echo ""
1284: echo " we emulate the atomic ${size}-bit ${op} with a compare-and-exchange: */"
1285: echo " else {"
1286: echo ""
1287: echo " /* do an atomic read of the memory: */"
1288: echo " value_read = tme_memory_atomic_read${size}(memory, rwlock, align_min);"
1289: echo ""
1290: echo " /* spin the ${op} in a compare-and-exchange loop: */"
1291: echo " for (;;) {"
1292: if test "x${op_operation}" != x; then
1293: echo ""
1294: echo " /* make the value to write: */"
1295: echo " value_written = ${op_operation};"
1296: fi
1297: echo ""
1298: echo " /* try the compare-and-exchange: */"
1299: echo " value_read_verify = tme_memory_atomic_cx${size}(memory, value_read, value_written, rwlock, align_min);"
1300: echo ""
1301: echo " /* if the compare-and-exchange failed: */"
1302: echo " if (__tme_predict_false(value_read_verify != value_read)) {"
1303: echo ""
1304: echo " /* loop with the new value read from the memory: */"
1305: echo " value_read = value_read_verify;"
1306: echo " continue;"
1307: echo " }"
1308: echo ""
1309: echo " /* stop now: */"
1310: echo " break;"
1311: echo " }"
1312: echo " }"
1313: fi
1314: if test ${op} != write; then
1315: echo ""
1316: echo " /* return the value read: */"
1317: echo " return (value_read);"
1318: fi
1319: echo "}"
1320: done
1321:
1322: if test `expr ${size} \>= ${size_ifdef}` = 1; then
1323: echo ""
1324: echo "#endif /* TME_HAVE_INT${size}_T */"
1325: fi
1326:
1327: done
1328:
1329: # done:
1330: #
1331: exit 0
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.