xref: /plan9/sys/src/cmd/gs/src/genarch.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1989-2004 artofcode LLC. All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: genarch.c,v 1.11 2004/06/17 21:42:53 giles Exp $ */
18 /*
19  * Generate a header file (arch.h) with parameters
20  * reflecting the machine architecture and compiler characteristics.
21  */
22 
23 #include "stdpre.h"
24 #include <ctype.h>
25 #include <stdio.h>
26 /*
27  * In theory, not all systems provide <string.h> or <setjmp.h>, or declare
28  * memset in <string.h>, but at this point I don't think we care about any
29  * that don't.
30  */
31 #include <string.h>
32 #include <time.h>
33 #include <setjmp.h>
34 
35 /* We should write the result on stdout, but the original Turbo C 'make' */
36 /* can't handle output redirection (sigh). */
37 
38 private void
section(FILE * f,const char * str)39 section(FILE * f, const char *str)
40 {
41     fprintf(f, "\n\t /* ---------------- %s ---------------- */\n\n", str);
42 }
43 
44 private clock_t
time_clear(char * buf,int bsize,int nreps)45 time_clear(char *buf, int bsize, int nreps)
46 {
47     clock_t t = clock();
48     int i;
49 
50     for (i = 0; i < nreps; ++i)
51 	memset(buf, 0, bsize);
52     return clock() - t;
53 }
54 
55 private void
define(FILE * f,const char * str)56 define(FILE *f, const char *str)
57 {
58     fprintf(f, "#define %s ", str);
59 }
60 
61 private void
define_int(FILE * f,const char * str,int value)62 define_int(FILE *f, const char *str, int value)
63 {
64     fprintf(f, "#define %s %d\n", str, value);
65 }
66 
67 private void
print_ffs(FILE * f,int nbytes)68 print_ffs(FILE *f, int nbytes)
69 {
70     int i;
71 
72     for (i = 0; i < nbytes; ++i)
73 	fprintf(f, "ff");
74 }
75 
76 private int
ilog2(int n)77 ilog2(int n)
78 {
79     int i = 0, m = n;
80 
81     while (m > 1)
82 	++i, m = (m + 1) >> 1;
83     return i;
84 }
85 
86 int
main(int argc,char * argv[])87 main(int argc, char *argv[])
88 {
89     char *fname = argv[1];
90     long one = 1;
91     struct {
92 	char c;
93 	short s;
94     } ss;
95     struct {
96 	char c;
97 	int i;
98     } si;
99     struct {
100 	char c;
101 	long l;
102     } sl;
103     struct {
104 	char c;
105 	char *p;
106     } sp;
107     struct {
108 	char c;
109 	float f;
110     } sf;
111     struct {
112 	char c;
113 	double d;
114     } sd;
115     /* Some architectures have special alignment requirements for jmpbuf. */
116     struct {
117 	char c;
118 	jmp_buf j;
119     } sj;
120     long lm1 = -1;
121     long lr1 = lm1 >> 1, lr2 = lm1 >> 2;
122     unsigned long um1 = ~(unsigned long)0;
123     int im1 = -1;
124     int ir1 = im1 >> 1, ir2 = im1 >> 2;
125     union {
126 	long l;
127 	char *p;
128     } pl0, pl1;
129     int ars;
130     int lwidth = size_of(long) * 8;
131     union {
132 	float f;
133 	int i;
134 	long l;
135     } f0, f1, fm1;
136     int floats_are_IEEE;
137     FILE *f = fopen(fname, "w");
138 
139     if (f == NULL) {
140 	fprintf(stderr, "genarch.c: can't open %s for writing\n", fname);
141 	return exit_FAILED;
142     }
143     fprintf(f, "/* Parameters derived from machine and compiler architecture. */\n");
144     fprintf(f, "/* This file is generated mechanically by genarch.c. */\n");
145 
146     /* We have to test the size dynamically here, */
147     /* because the preprocessor can't evaluate sizeof. */
148     f0.f = 0.0, f1.f = 1.0, fm1.f = -1.0;
149     floats_are_IEEE =
150 	(size_of(float) == size_of(int) ?
151 	 f0.i == 0 && f1.i == (int)0x3f800000 && fm1.i == (int)0xbf800000 :
152 	 f0.l == 0 && f1.l == 0x3f800000L && fm1.l == 0xbf800000L);
153 
154     section(f, "Scalar alignments");
155 
156 #define OFFSET_IN(s, e) (int)((char *)&s.e - (char *)&s)
157     define_int(f, "ARCH_ALIGN_SHORT_MOD", OFFSET_IN(ss, s));
158     define_int(f, "ARCH_ALIGN_INT_MOD", OFFSET_IN(si, i));
159     define_int(f, "ARCH_ALIGN_LONG_MOD", OFFSET_IN(sl, l));
160     define_int(f, "ARCH_ALIGN_PTR_MOD", OFFSET_IN(sp, p));
161     define_int(f, "ARCH_ALIGN_FLOAT_MOD", OFFSET_IN(sf, f));
162     define_int(f, "ARCH_ALIGN_DOUBLE_MOD", OFFSET_IN(sd, d));
163     define_int(f, "ARCH_ALIGN_STRUCT_MOD", OFFSET_IN(sj, j));
164 #undef OFFSET_IN
165 
166     section(f, "Scalar sizes");
167 
168     define_int(f, "ARCH_LOG2_SIZEOF_CHAR", ilog2(size_of(char)));
169     define_int(f, "ARCH_LOG2_SIZEOF_SHORT", ilog2(size_of(short)));
170     define_int(f, "ARCH_LOG2_SIZEOF_INT", ilog2(size_of(int)));
171     define_int(f, "ARCH_LOG2_SIZEOF_LONG", ilog2(size_of(long)));
172 #ifdef HAVE_LONG_LONG
173     define_int(f, "ARCH_LOG2_SIZEOF_LONG_LONG", ilog2(size_of(long long)));
174 #endif
175     define_int(f, "ARCH_SIZEOF_PTR", size_of(char *));
176     define_int(f, "ARCH_SIZEOF_FLOAT", size_of(float));
177     define_int(f, "ARCH_SIZEOF_DOUBLE", size_of(double));
178     if (floats_are_IEEE) {
179 	define_int(f, "ARCH_FLOAT_MANTISSA_BITS", 24);
180 	define_int(f, "ARCH_DOUBLE_MANTISSA_BITS", 53);
181     } else {
182 	/*
183 	 * There isn't any general way to compute the number of mantissa
184 	 * bits accurately, especially if the machine uses hex rather
185 	 * than binary exponents.  Use conservative values, assuming
186 	 * the exponent is stored in a 16-bit word of its own.
187 	 */
188 	define_int(f, "ARCH_FLOAT_MANTISSA_BITS", sizeof(float) * 8 - 17);
189 	define_int(f, "ARCH_DOUBLE_MANTISSA_BITS", sizeof(double) * 8 - 17);
190     }
191 
192     section(f, "Unsigned max values");
193 
194     /*
195      * We can't use fprintf with a numeric value for PRINT_MAX, because
196      * too many compilers produce warnings or do the wrong thing for
197      * complementing or widening unsigned types.
198      */
199 #define PRINT_MAX(str, typ, tstr, l)\
200   BEGIN\
201     define(f, str);\
202     fprintf(f, "((%s)0x", tstr);\
203     print_ffs(f, sizeof(typ));\
204     fprintf(f, "%s + (%s)0)\n", l, tstr);\
205   END
206     PRINT_MAX("ARCH_MAX_UCHAR", unsigned char, "unsigned char", "");
207     PRINT_MAX("ARCH_MAX_USHORT", unsigned short, "unsigned short", "");
208     /*
209      * For uint and ulong, a different approach is required to keep gcc
210      * with -Wtraditional from spewing out pointless warnings.
211      */
212     define(f, "ARCH_MAX_UINT");
213     fprintf(f, "((unsigned int)~0 + (unsigned int)0)\n");
214     define(f, "ARCH_MAX_ULONG");
215     fprintf(f, "((unsigned long)~0L + (unsigned long)0)\n");
216 #undef PRINT_MAX
217 
218     section(f, "Cache sizes");
219 
220     /*
221      * Determine the primary and secondary cache sizes by looking for a
222      * non-linearity in the time required to fill blocks with memset.
223      */
224     {
225 #define MAX_BLOCK (1 << 22)	/* max 4M cache */
226 #define MAX_NREPS (1 << 10)	/* limit the number of reps we try */
227 	static char buf[MAX_BLOCK];
228 	int bsize = 1 << 10;
229 	int nreps = 1;
230 	clock_t t = 0;
231 	clock_t t_eps;
232 
233 	/*
234 	 * Increase the number of repetitions until the time is
235 	 * long enough to exceed the likely uncertainty.
236 	 */
237 
238 	while (nreps < MAX_NREPS && (t = time_clear(buf, bsize, nreps)) == 0)
239 	    nreps <<= 1;
240 	t_eps = t;
241 	while (nreps < MAX_NREPS && (t = time_clear(buf, bsize, nreps)) < t_eps * 10)
242 	    nreps <<= 1;
243 
244 	/*
245 	 * Increase the block size until the time jumps non-linearly.
246 	 */
247 	for (; bsize <= MAX_BLOCK;) {
248 	    clock_t dt = time_clear(buf, bsize, nreps);
249 
250 	    if (dt > t + (t >> 1)) {
251 		t = dt;
252 		break;
253 	    }
254 	    bsize <<= 1;
255 	    nreps >>= 1;
256 	    if (nreps == 0)
257 		nreps = 1, t <<= 1;
258 	}
259 	define_int(f, "ARCH_CACHE1_SIZE", bsize >> 1);
260 	/*
261 	 * Do the same thing a second time for the secondary cache.
262 	 */
263 	if (nreps > 1)
264 	    nreps >>= 1, t >>= 1;
265 	for (; bsize <= MAX_BLOCK;) {
266 	    clock_t dt = time_clear(buf, bsize, nreps);
267 
268 	    if (dt > t * 1.25) {
269 		t = dt;
270 		break;
271 	    }
272 	    bsize <<= 1;
273 	    nreps >>= 1;
274 	    if (nreps == 0)
275 		nreps = 1, t <<= 1;
276 	}
277 	define_int(f, "ARCH_CACHE2_SIZE", bsize >> 1);
278     }
279 
280     section(f, "Miscellaneous");
281 
282     define_int(f, "ARCH_IS_BIG_ENDIAN", 1 - *(char *)&one);
283     pl0.l = 0;
284     pl1.l = -1;
285     define_int(f, "ARCH_PTRS_ARE_SIGNED", (pl1.p < pl0.p));
286     define_int(f, "ARCH_FLOATS_ARE_IEEE", (floats_are_IEEE ? 1 : 0));
287 
288     /*
289      * There are three cases for arithmetic right shift:
290      * always correct, correct except for right-shifting a long by 1
291      * (a bug in some versions of the Turbo C compiler), and
292      * never correct.
293      */
294     ars = (lr2 != -1 || ir1 != -1 || ir2 != -1 ? 0 :
295 	   lr1 != -1 ? 1 :	/* Turbo C problem */
296 	   2);
297     define_int(f, "ARCH_ARITH_RSHIFT", ars);
298     /*
299      * Some machines can't handle a variable shift by
300      * the full width of a long.
301      */
302     define_int(f, "ARCH_CAN_SHIFT_FULL_LONG", um1 >> lwidth == 0);
303     /*
304      * Determine whether dividing a negative integer by a positive one
305      * takes the floor or truncates toward zero.
306      */
307     define_int(f, "ARCH_DIV_NEG_POS_TRUNCATES", im1 / 2 == 0);
308 
309 /* ---------------- Done. ---------------- */
310 
311     fclose(f);
312     return exit_OK;
313 }
314