/* @(#)align_test.c	1.26 09/11/07 Copyright 1995-2009 J. Schilling */
#include <schily/mconfig.h>
#ifndef	lint
static	UConst char sccsid[] =
	"@(#)align_test.c	1.26 09/11/07 Copyright 1995-2009 J. Schilling";
#endif
/*
 *	Generate machine dependant align.h
 *
 *	Copyright (c) 1995-2009 J. Schilling
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

#include <schily/stdio.h>
#include <schily/standard.h>
#undef	NO_LONGLONG	/* Make sure that Llong wil be long long */
#include <schily/utypes.h>

/*
 * Be very careful here as MSVC does not implement long long but rather __int64
 * and once someone makes 'long long' 128 bits on a 64 bit machine, we need to
 * check for a MSVC __int128 type.
 */

/*
 * CHECK_ALIGN needs SIGBUS, but DJGPP has no SIGBUS
 */
/*#define	FORCE_ALIGN*/
/*#define	OFF_ALIGN*/
/*#define	CHECK_ALIGN*/

EXPORT	int	main	__PR((int ac, char **av));

#if	!defined(FORCE_ALIGN) && !defined(OFF_ALIGN) &&	!defined(CHECK_ALIGN)
#define	OFF_ALIGN
#endif

char	buf[8192+1024];
char	*buf_aligned;

#ifdef	FORCE_ALIGN
#	undef	CHECK_ALIGN
#	undef	OFF_ALIGN
#endif

#ifdef	CHECK_ALIGN
#	undef	FORCE_ALIGN
#	undef	OFF_ALIGN
#endif

#ifdef	OFF_ALIGN
#	undef	FORCE_ALIGN
#	undef	CHECK_ALIGN
#endif


#ifdef	FORCE_ALIGN

#define	ALIGN_short	sizeof (short)
#define	ALIGN_int	sizeof (int)
#define	ALIGN_long	sizeof (long)
#define	ALIGN_longlong	sizeof (Llong)
#define	ALIGN_float	sizeof (float)
#define	ALIGN_double	sizeof (double)
#define	ALIGN_ldouble	sizeof (long double)
#define	ALIGN_ptr	sizeof (char *)

#endif

#ifdef	CHECK_ALIGN

#include <schily/signal.h>
#include <schily/setjmp.h>
LOCAL	jmp_buf	jb;

LOCAL	int	check_align	__PR((int (*)(char *, int),
					void (*)(char *, int), int));
LOCAL	int	check_short	__PR((char *, int));
LOCAL	int	check_int	__PR((char *, int));
LOCAL	int	check_long	__PR((char *, int));
LOCAL	int	check_longlong	__PR((char *, int));
LOCAL	int	check_float	__PR((char *, int));
LOCAL	int	check_double	__PR((char *, int));
#ifdef	HAVE_LONGDOUBLE
LOCAL	int	check_ldouble	__PR((char *, int));
#endif
LOCAL	int	check_ptr	__PR((char *, int));

LOCAL	int	speed_check	__PR((char *,
					void (*)(char *, int), int));
LOCAL	void	speed_short	__PR((char *, int));
LOCAL	void	speed_int	__PR((char *, int));
LOCAL	void	speed_long	__PR((char *, int));
LOCAL	void	speed_longlong	__PR((char *, int));
LOCAL	void	speed_float	__PR((char *, int));
LOCAL	void	speed_double	__PR((char *, int));
#ifdef	HAVE_LONGDOUBLE
LOCAL	void	speed_ldouble	__PR((char *, int));
#endif
LOCAL	void	speed_ptr	__PR((char *, int));

#define	ALIGN_short	check_align(check_short, speed_short, sizeof (short))
#define	ALIGN_int	check_align(check_int, speed_int, sizeof (int))
#define	ALIGN_long	check_align(check_long, speed_long, sizeof (long))
#define	ALIGN_longlong	check_align(check_longlong, speed_longlong, sizeof (Llong))
#define	ALIGN_float	check_align(check_float, speed_float, sizeof (float))
#define	ALIGN_double	check_align(check_double, speed_double, sizeof (double))
#define	ALIGN_ldouble	check_align(check_ldouble, speed_ldouble, sizeof (long double))
#define	ALIGN_ptr	check_align(check_ptr, speed_ptr, sizeof (char *))

#endif

#ifdef	OFF_ALIGN

#define	sm_off(s, m)	((size_t)&((s)0)->m)

LOCAL	int	off_short	__PR((void));
LOCAL	int	off_int		__PR((void));
LOCAL	int	off_long	__PR((void));
LOCAL	int	off_longlong	__PR((void));
LOCAL	int	off_float	__PR((void));
LOCAL	int	off_double	__PR((void));
#ifdef	HAVE_LONGDOUBLE
LOCAL	int	off_ldouble	__PR((void));
#endif
LOCAL	int	off_ptr		__PR((void));

#define	ALIGN_short	off_short()
#define	ALIGN_int	off_int()
#define	ALIGN_long	off_long()
#define	ALIGN_longlong	off_longlong()
#define	ALIGN_float	off_float()
#define	ALIGN_double	off_double()
#define	ALIGN_ldouble	off_ldouble()
#define	ALIGN_ptr	off_ptr()

#endif

LOCAL	void	printmacs	__PR((void));

#ifdef	CHECK_ALIGN
LOCAL	void	sig		__PR((int));

LOCAL void
sig(signo)
	int	signo;
{
	signal(signo, sig);
	longjmp(jb, 1);
}
#endif

#if defined(mc68000) || defined(mc68020)
#define	MIN_ALIGN	2
#else
#define	MIN_ALIGN	2
#endif


#define	min_align(i)	(((i) < MIN_ALIGN) ? MIN_ALIGN : (i))

/*
 * Make it LOCAL MacOS-X by default links against libcurses and
 * so we totherwise have a double defined "al".
 */
LOCAL	char	al[] = "alignment value for ";
LOCAL	char	ms[] = "alignment mask  for ";
LOCAL	char	so[] = "sizeof ";
LOCAL	char	sh[] = "short";
LOCAL	char	in[] = "int";
LOCAL	char	lo[] = "long";
LOCAL	char	ll[] = "long long";
LOCAL	char	fl[] = "float";
LOCAL	char	db[] = "double";
LOCAL	char	ld[] = "long double";
LOCAL	char	pt[] = "pointer";
LOCAL	char	mt[] = "max type";

#define	xalign(x, a, m)		(((char *)(x)) + ((a) - (((UIntptr_t)(x))&(m))))

EXPORT int
main(ac, av)
	int	ac;
	char	**av;
{
	char	*p;
	int	i;
	int	s;
	int	amax = 0;
	int	smax = 0;

#ifdef	CHECK_ALIGN
#ifdef	SIGBUS
	signal(SIGBUS, sig);
#endif
#endif

	i = ((size_t)buf) % 1024;
	i = 1024 - i;
	p = &buf[i];
	buf_aligned = p;

#ifdef	DEBUG
	fprintf(stderr, "buf: 0x%lX 0x%lX\n",
		(unsigned long)buf, (unsigned long)xalign(buf, 1024, 1023));
#endif

	printf("/*\n");
	printf(" * This file has been generated automatically\n");
	printf(" * by %s\n", sccsid);
	printf(" * do not edit by hand.\n");
	printf(" */\n");

	s = sizeof (short);
	i  = ALIGN_short;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_SHORT	%d\t/* %s(%s *)\t*/\n", i, al, sh);
	printf("#define	ALIGN_SMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, sh);
	printf("#define	SIZE_SHORT	%d\t/* %s(%s)\t\t\t*/\n", s, so, sh);

	s = sizeof (int);
	i  = ALIGN_int;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_INT	%d\t/* %s(%s *)\t\t*/\n", i, al, in);
	printf("#define	ALIGN_IMASK	%d\t/* %s(%s *)\t\t*/\n", i-1, ms, in);
	printf("#define	SIZE_INT	%d\t/* %s(%s)\t\t\t\t*/\n", s, so, in);

	s = sizeof (long);
	i  = ALIGN_long;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_LONG	%d\t/* %s(%s *)\t\t*/\n", i, al, lo);
	printf("#define	ALIGN_LMASK	%d\t/* %s(%s *)\t\t*/\n", i-1, ms, lo);
	printf("#define	SIZE_LONG	%d\t/* %s(%s)\t\t\t*/\n", s, so, lo);

#ifdef	HAVE_LONGLONG
	s = sizeof (Llong);
	i  = ALIGN_longlong;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
#endif
	printf("\n");
	printf("#define	ALIGN_LLONG	%d\t/* %s(%s *)\t*/\n", i, al, ll);
	printf("#define	ALIGN_LLMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, ll);
	printf("#define	SIZE_LLONG	%d\t/* %s(%s)\t\t\t*/\n", s, so, ll);

	s = sizeof (float);
	i  = ALIGN_float;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_FLOAT	%d\t/* %s(%s *)\t*/\n", i, al, fl);
	printf("#define	ALIGN_FMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, fl);
	printf("#define	SIZE_FLOAT	%d\t/* %s(%s)\t\t\t*/\n", s, so, fl);

	s = sizeof (double);
	i  = ALIGN_double;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_DOUBLE	%d\t/* %s(%s *)\t*/\n", i, al, db);
	printf("#define	ALIGN_DMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, db);
	printf("#define	SIZE_DOUBLE	%d\t/* %s(%s)\t\t\t*/\n", s, so, db);

#ifdef	HAVE_LONGDOUBLE
	s = sizeof (long double);
	i  = ALIGN_ldouble;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
#endif
	printf("\n");
	printf("#define	ALIGN_LDOUBLE	%d\t/* %s(%s *)\t*/\n", i, al, ld);
	printf("#define	ALIGN_LDMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, ld);
	printf("#define	SIZE_LDOUBLE	%d\t/* %s(%s)\t\t\t*/\n", s, so, ld);

	s = sizeof (char *);
	i  = ALIGN_ptr;
	i = min_align(i);
	if (i > amax)
		amax = i;
	if (s > smax)
		smax = s;
	printf("\n");
	printf("#define	ALIGN_PTR	%d\t/* %s(%s *)\t*/\n", i, al, pt);
	printf("#define	ALIGN_PMASK	%d\t/* %s(%s *)\t*/\n", i-1, ms, pt);
	printf("#define	SIZE_PTR	%d\t/* %s(%s)\t\t\t*/\n", s, so, pt);

	printf("\n");
	printf("#define	ALIGN_TMAX	%d\t/* %s(%s *)\t*/\n", amax, al, mt);
	printf("#define	ALIGN_TMMASK	%d\t/* %s(%s *)\t*/\n", amax-1, ms, mt);
	printf("#define	SIZE_TMAX	%d\t/* %s(%s)\t\t\t*/\n", smax, so, mt);

	printmacs();
	return (0);
}

LOCAL void
printmacs()
{
printf("\n\n");
printf("/*\n * There used to be a cast to an int but we get a warning from GCC.\n");
printf(" * This warning message from GCC is wrong.\n");
printf(" * Believe me that this macro would even be usable if I would cast to short.\n");
printf(" * In order to avoid this warning, we are now using UIntptr_t\n */\n");
/*printf("\n");*/
/*printf("\n");*/
printf("#define	xaligned(a, s)		((((UIntptr_t)(a)) & (s)) == 0 )\n");
printf("#define	x2aligned(a, b, s)	(((((UIntptr_t)(a)) | ((UIntptr_t)(b))) & (s)) == 0 )\n");
printf("\n");
printf("#define	saligned(a)		xaligned(a, ALIGN_SMASK)\n");
printf("#define	s2aligned(a, b)		x2aligned(a, b, ALIGN_SMASK)\n");
printf("\n");
printf("#define	ialigned(a)		xaligned(a, ALIGN_IMASK)\n");
printf("#define	i2aligned(a, b)		x2aligned(a, b, ALIGN_IMASK)\n");
printf("\n");
printf("#define	laligned(a)		xaligned(a, ALIGN_LMASK)\n");
printf("#define	l2aligned(a, b)		x2aligned(a, b, ALIGN_LMASK)\n");
printf("\n");
printf("#define	llaligned(a)		xaligned(a, ALIGN_LLMASK)\n");
printf("#define	ll2aligned(a, b)	x2aligned(a, b, ALIGN_LLMASK)\n");
printf("\n");
printf("#define	faligned(a)		xaligned(a, ALIGN_FMASK)\n");
printf("#define	f2aligned(a, b)		x2aligned(a, b, ALIGN_FMASK)\n");
printf("\n");
printf("#define	daligned(a)		xaligned(a, ALIGN_DMASK)\n");
printf("#define	d2aligned(a, b)		x2aligned(a, b, ALIGN_DMASK)\n");
printf("\n");
printf("#define	ldaligned(a)		xaligned(a, ALIGN_LDMASK)\n");
printf("#define	ld2aligned(a, b)	x2aligned(a, b, ALIGN_LDMASK)\n");
printf("\n");
printf("#define	paligned(a)		xaligned(a, ALIGN_PMASK)\n");
printf("#define	p2aligned(a, b)		x2aligned(a, b, ALIGN_PMASK)\n");
printf("\n");
printf("#define	maligned(a)		xaligned(a, ALIGN_TMMASK)\n");
printf("#define	m2aligned(a, b)		x2aligned(a, b, ALIGN_TMMASK)\n");

printf("\n\n");
printf("/*\n * There used to be a cast to an int but we get a warning from GCC.\n");
printf(" * This warning message from GCC is wrong.\n");
printf(" * Believe me that this macro would even be usable if I would cast to short.\n");
printf(" * In order to avoid this warning, we are now using UIntptr_t\n */\n");
printf("#define	xalign(x, a, m)		( ((char *)(x)) + ( (a) - 1 - ((((UIntptr_t)(x))-1)&(m))) )\n");
printf("\n");
printf("#define	salign(x)		xalign((x), ALIGN_SHORT, ALIGN_SMASK)\n");
printf("#define	ialign(x)		xalign((x), ALIGN_INT, ALIGN_IMASK)\n");
printf("#define	lalign(x)		xalign((x), ALIGN_LONG, ALIGN_LMASK)\n");
printf("#define	llalign(x)		xalign((x), ALIGN_LLONG, ALIGN_LLMASK)\n");
printf("#define	falign(x)		xalign((x), ALIGN_FLOAT, ALIGN_FMASK)\n");
printf("#define	dalign(x)		xalign((x), ALIGN_DOUBLE, ALIGN_DMASK)\n");
printf("#define	ldalign(x)		xalign((x), ALIGN_LDOUBLE, ALIGN_LDMASK)\n");
printf("#define	palign(x)		xalign((x), ALIGN_PTR, ALIGN_PMASK)\n");
printf("#define	malign(x)		xalign((x), ALIGN_TMAX, ALIGN_TMMASK)\n");
}

#ifdef	CHECK_ALIGN
/*
 * Routines to compute the alignement by checking if the assignement
 * causes a bus error.
 * Some systems (e.g. Linux on DEC Aplha) will allow to fetch any
 * type from any address. On these systems we must check the speed
 * because unaligned fetches will take more time.
 */
LOCAL int
check_align(cfunc, sfunc, tsize)
	int	(*cfunc)();
	void	(*sfunc)();
	int	tsize;
{
		int	calign;
		int	align;
		int	tcheck;
		int	t;
	register int	i;
	register char	*p = buf_aligned;

	for (i = 1; i < 128; i++) {
		if (!setjmp(jb)) {
			(cfunc)(p, i);
			break;
		}
	}
#ifdef	DEBUG
	fprintf(stderr, "i: %d tsize: %d\n", i, tsize);
#endif
	if (i == tsize)
		return (i);

	align = calign = i;
	tcheck = speed_check(p, sfunc, i);
#ifdef	DEBUG
	fprintf(stderr, "tcheck: %d\n", tcheck);
#endif

	for (i = calign*2; i <= tsize; i *= 2) {
		t = speed_check(p, sfunc, i);
#ifdef	DEBUG
		fprintf(stderr, "tcheck: %d t: %d i: %d\n", tcheck, t, i);
		fprintf(stderr, "tcheck - t: %d ... * 3: %d\n",  (tcheck - t), (tcheck - t) * 3);
#endif
		if (((tcheck - t) > 0) && ((tcheck - t) * 3) > tcheck) {
#ifdef	DEBUG
			fprintf(stderr, "kleiner\n");
#endif
			align = i;
			tcheck = t;
		}
	}
	return (align);
}

LOCAL int
check_short(p, i)
	char	*p;
	int	i;
{
	short	*sp;

	sp = (short *)&p[i];
	*sp = 1;
	return (0);
}

LOCAL int
check_int(p, i)
	char	*p;
	int	i;
{
	int	*ip;

	ip = (int *)&p[i];
	*ip = 1;
	return (0);
}

LOCAL int
check_long(p, i)
	char	*p;
	int	i;
{
	long	*lp;

	lp = (long *)&p[i];
	*lp = 1;
	return (0);
}

#ifdef	HAVE_LONGLONG
LOCAL int
check_longlong(p, i)
	char	*p;
	int	i;
{
	Llong	*llp;

	llp = (Llong *)&p[i];
	*llp = 1;
	return (0);
}
#endif

LOCAL int
check_float(p, i)
	char	*p;
	int	i;
{
	float	*fp;

	fp = (float *)&p[i];
	*fp = 1.0;
	return (0);
}

LOCAL int
check_double(p, i)
	char	*p;
	int	i;
{
	double	*dp;

	dp = (double *)&p[i];
	*dp = 1.0;
	return (0);
}

#ifdef	HAVE_LONGDOUBLE
LOCAL int
check_ldouble(p, i)
	char	*p;
	int	i;
{
	long double	*dp;

	dp = (long double *)&p[i];
	*dp = 1.0;
	return (0);
}
#endif

LOCAL int
check_ptr(p, i)
	char	*p;
	int	i;
{
	char	**pp;

	pp = (char  **)&p[i];
	*pp = (char *)1;
	return (0);
}

/*
 * Routines to compute the alignement by checking the speed of the
 * assignement.
 * Some systems (e.g. Linux on DEC Aplha) will allow to fetch any
 * type from any address. On these systems we must check the speed
 * because unaligned fetches will take more time.
 */
LOCAL void
speed_short(p, n)
	char	*p;
	int	n;
{
	short	*sp;
	int	i;

	sp = (short *)&p[n];

	for (i = 1000000; --i >= 0; )
		*sp = i;
}

LOCAL void
speed_int(p, n)
	char	*p;
	int	n;
{
	int	*ip;
	int	i;

	ip = (int *)&p[n];

	for (i = 1000000; --i >= 0; )
		*ip = i;
}

LOCAL void
speed_long(p, n)
	char	*p;
	int	n;
{
	long	*lp;
	int	i;

	lp = (long *)&p[n];

	for (i = 1000000; --i >= 0; )
		*lp = i;
}

#ifdef	HAVE_LONGLONG
LOCAL void
speed_longlong(p, n)
	char	*p;
	int	n;
{
	Llong *llp;
	int	i;

	llp = (Llong *)&p[n];

	for (i = 1000000; --i >= 0; )
		*llp = i;
}
#endif

LOCAL void
speed_float(p, n)
	char	*p;
	int	n;
{
	float	*fp;
	int	i;

	fp = (float *)&p[n];

	for (i = 1000000; --i >= 0; )
		*fp = i;
}

LOCAL void
speed_double(p, n)
	char	*p;
	int	n;
{
	double	*dp;
	int	i;

	dp = (double *)&p[n];

	for (i = 1000000; --i >= 0; )
		*dp = i;
}

#ifdef	HAVE_LONGDOUBLE
LOCAL void
speed_ldouble(p, n)
	char	*p;
	int	n;
{
	long double	*dp;
	int	i;

	dp = (long double *)&p[n];

	for (i = 1000000; --i >= 0; )
		*dp = i;
}
#endif

LOCAL void
speed_ptr(p, n)
	char	*p;
	int	n;
{
	char	**pp;
	int	i;

	pp = (char **)&p[n];

	for (i = 1000000; --i >= 0; )
		*pp = (char *)i;
}

#include <schily/time.h>
#include <schily/times.h>
LOCAL int
speed_check(p, sfunc, n)
	char	*p;
	void	(*sfunc)();
	int	n;
{
	struct tms tm1;
	struct tms tm2;

	times(&tm1);
	(*sfunc)(p, n);
	times(&tm2);

#ifdef	DEBUG
	fprintf(stderr, "t1: %ld\n", (long) tm2.tms_utime-tm1.tms_utime);
#endif

	return ((int) tm2.tms_utime-tm1.tms_utime);
}

#endif	/* CHECK_ALIGN */

#ifdef	OFF_ALIGN
/*
 * Routines to compute the alignement by using the knowledge
 * of the C-compiler.
 * We define a structure and check the padding that has been inserted
 * by the compiler to keep the apropriate type on a properly aligned
 * address.
 */
LOCAL int
off_short()
{
	struct ss {
		char	c;
		short	s;
	} ss;
	ss.c = 0;		/* fool C-compiler */

	return (sm_off(struct ss *, s));
}

LOCAL int
off_int()
{
	struct si {
		char	c;
		int	i;
	} si;
	si.c = 0;		/* fool C-compiler */

	return (sm_off(struct si *, i));
}

LOCAL int
off_long()
{
	struct sl {
		char	c;
		long	l;
	} sl;
	sl.c = 0;		/* fool C-compiler */

	return (sm_off(struct sl *, l));
}

#ifdef	HAVE_LONGLONG
LOCAL int
off_longlong()
{
	struct sll {
		char	c;
		Llong	ll;
	} sll;
	sll.c = 0;		/* fool C-compiler */

	return (sm_off(struct sll *, ll));
}
#endif

LOCAL int
off_float()
{
	struct sf {
		char	c;
		float	f;
	} sf;
	sf.c = 0;		/* fool C-compiler */

	return (sm_off(struct sf *, f));
}

LOCAL int
off_double()
{
	struct sd {
		char	c;
		double	d;
	} sd;
	sd.c = 0;		/* fool C-compiler */

	return (sm_off(struct sd *, d));
}

#ifdef	HAVE_LONGDOUBLE
LOCAL int
off_ldouble()
{
	struct sd {
		char		c;
		long double	ld;
	} sd;
	sd.c = 0;		/* fool C-compiler */

	return (sm_off(struct sd *, ld));
}
#endif

LOCAL int
off_ptr()
{
	struct sp {
		char	c;
		char	*p;
	} sp;
	sp.c = 0;		/* fool C-compiler */

	return (sm_off(struct sp *, p));
}

#endif	/* OFF_ALIGN */
