/* Copyright (C) 1989, 1995, 1996, 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: genarch.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Generate a header file (arch.h) with parameters */ /* reflecting the machine architecture and compiler characteristics. */ #include "stdpre.h" #include #include /* * In theory, not all systems provide , or declare memset in * that header file, but at this point I don't think we care about any * that don't. */ #include #include /* We should write the result on stdout, but the original Turbo C 'make' */ /* can't handle output redirection (sigh). */ private void section(FILE * f, const char *str) { fprintf(f, "\n\t /* ---------------- %s ---------------- */\n\n", str); } #ifndef __FreeBSD__ private clock_t time_clear(char *buf, int bsize, int nreps) { clock_t t = clock(); int i; for (i = 0; i < nreps; ++i) memset(buf, 0, bsize); return clock() - t; } #endif /* __FreeBSD__ */ private void define(FILE *f, const char *str) { fprintf(f, "#define %s ", str); } private void define_int(FILE *f, const char *str, int value) { fprintf(f, "#define %s %d\n", str, value); } const char ff_str[] = "ffffffffffffffff"; /* 8 bytes */ int main(int argc, char *argv[]) { char *fname = argv[1]; long one = 1; int ff_strlen = sizeof(ff_str) - 1; struct { char c; short s; } ss; struct { char c; int i; } si; struct { char c; long l; } sl; struct { char c; char *p; } sp; struct { char c; float f; } sf; struct { char c; double d; } sd; static int log2s[17] = {0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4}; long lm1 = -1; long lr1 = lm1 >> 1, lr2 = lm1 >> 2; unsigned long um1 = ~(unsigned long)0; int im1 = -1; int ir1 = im1 >> 1, ir2 = im1 >> 2; union { long l; char *p; } pl0, pl1; int ars; int lwidth = size_of(long) * 8; union { float f; int i; long l; } f0 , f1, fm1; int floats_are_IEEE; FILE *f = fopen(fname, "w"); if (f == NULL) { fprintf(stderr, "genarch.c: can't open %s for writing\n", fname); return exit_FAILED; } fprintf(f, "/* Parameters derived from machine and compiler architecture */\n"); /* We have to test the size dynamically here, */ /* because the preprocessor can't evaluate sizeof. */ f0.f = 0.0, f1.f = 1.0, fm1.f = -1.0; floats_are_IEEE = (size_of(float) == size_of(int) ? f0.i == 0 && f1.i == (int)0x3f800000 && fm1.i == (int)0xbf800000 : f0.l == 0 && f1.l == 0x3f800000L && fm1.l == 0xbf800000L); section(f, "Scalar alignments"); #define OFFSET_IN(s, e) (int)((char *)&s.e - (char *)&s) define_int(f, "ARCH_ALIGN_SHORT_MOD", OFFSET_IN(ss, s)); define_int(f, "ARCH_ALIGN_INT_MOD", OFFSET_IN(si, i)); define_int(f, "ARCH_ALIGN_LONG_MOD", OFFSET_IN(sl, l)); define_int(f, "ARCH_ALIGN_PTR_MOD", OFFSET_IN(sp, p)); define_int(f, "ARCH_ALIGN_FLOAT_MOD", OFFSET_IN(sf, f)); define_int(f, "ARCH_ALIGN_DOUBLE_MOD", OFFSET_IN(sd, d)); #undef OFFSET_IN section(f, "Scalar sizes"); define_int(f, "ARCH_LOG2_SIZEOF_SHORT", log2s[size_of(short)]); define_int(f, "ARCH_LOG2_SIZEOF_INT", log2s[size_of(int)]); define_int(f, "ARCH_LOG2_SIZEOF_LONG", log2s[size_of(long)]); define_int(f, "ARCH_SIZEOF_PTR", size_of(char *)); define_int(f, "ARCH_SIZEOF_FLOAT", size_of(float)); define_int(f, "ARCH_SIZEOF_DOUBLE", size_of(double)); if (floats_are_IEEE) { define_int(f, "ARCH_FLOAT_MANTISSA_BITS", 24); define_int(f, "ARCH_DOUBLE_MANTISSA_BITS", 53); } else { /* * There isn't any general way to compute the number of mantissa * bits accurately, especially if the machine uses hex rather * than binary exponents. Use conservative values, assuming * the exponent is stored in a 16-bit word of its own. */ define_int(f, "ARCH_FLOAT_MANTISSA_BITS", sizeof(float) * 8 - 17); define_int(f, "ARCH_DOUBLE_MANTISSA_BITS", sizeof(double) * 8 - 17); } section(f, "Unsigned max values"); #define PRINT_MAX(str, typ, tstr, l)\ define(f, str);\ fprintf(f, "((%s)0x%s%s + (%s)0)\n",\ tstr, ff_str + ff_strlen - size_of(typ) * 2, l, tstr) PRINT_MAX("ARCH_MAX_UCHAR", unsigned char, "unsigned char", ""); PRINT_MAX("ARCH_MAX_USHORT", unsigned short, "unsigned short", ""); /* * For uint and ulong, a different approach is required to keep gcc * with -Wtraditional from spewing out pointless warnings. */ define(f, "ARCH_MAX_UINT"); fprintf(f, "((unsigned int)~0 + (unsigned int)0)\n"); define(f, "ARCH_MAX_ULONG"); fprintf(f, "((unsigned long)~0L + (unsigned long)0)\n"); #undef PRINT_MAX #ifndef __FreeBSD__ section(f, "Cache sizes"); /* * Determine the primary and secondary cache sizes by looking for a * non-linearity in the time required to fill blocks with memset. */ { #define MAX_BLOCK (1 << 20) static char buf[MAX_BLOCK]; int bsize = 1 << 10; int nreps = 1; clock_t t = 0; clock_t t_eps; /* * Increase the number of repetitions until the time is * long enough to exceed the likely uncertainty. */ while ((t = time_clear(buf, bsize, nreps)) == 0) nreps <<= 1; t_eps = t; while ((t = time_clear(buf, bsize, nreps)) < t_eps * 10) nreps <<= 1; /* * Increase the block size until the time jumps non-linearly. */ for (; bsize <= MAX_BLOCK;) { clock_t dt = time_clear(buf, bsize, nreps); if (dt > t + (t >> 1)) { t = dt; break; } bsize <<= 1; nreps >>= 1; if (nreps == 0) nreps = 1, t <<= 1; } define_int(f, "ARCH_CACHE1_SIZE", bsize >> 1); /* * Do the same thing a second time for the secondary cache. */ if (nreps > 1) nreps >>= 1, t >>= 1; for (; bsize <= MAX_BLOCK;) { clock_t dt = time_clear(buf, bsize, nreps); if (dt > t * 1.25) { t = dt; break; } bsize <<= 1; nreps >>= 1; if (nreps == 0) nreps = 1, t <<= 1; } define_int(f, "ARCH_CACHE2_SIZE", bsize >> 1); } #endif /* __FreeBSD__ */ section(f, "Miscellaneous"); define_int(f, "ARCH_IS_BIG_ENDIAN", 1 - *(char *)&one); pl0.l = 0; pl1.l = -1; define_int(f, "ARCH_PTRS_ARE_SIGNED", (pl1.p < pl0.p)); define_int(f, "ARCH_FLOATS_ARE_IEEE", (floats_are_IEEE ? 1 : 0)); /* * There are three cases for arithmetic right shift: * always correct, correct except for right-shifting a long by 1 * (a bug in some versions of the Turbo C compiler), and * never correct. */ ars = (lr2 != -1 || ir1 != -1 || ir2 != -1 ? 0 : lr1 != -1 ? 1 : /* Turbo C problem */ 2); define_int(f, "ARCH_ARITH_RSHIFT", ars); /* * Some machines can't handle a variable shift by * the full width of a long. */ define_int(f, "ARCH_CAN_SHIFT_FULL_LONG", um1 >> lwidth == 0); /* * Determine whether dividing a negative integer by a positive one * takes the floor or truncates toward zero. */ define_int(f, "ARCH_DIV_NEG_POS_TRUNCATES", im1 / 2 == 0); /* ---------------- Done. ---------------- */ fclose(f); return exit_OK; }