Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 17 Jan 2021 22:25:08 +0100
From:      Walter von Entferndt <walter.von.entferndt@posteo.net>
To:        Mark Millard <marklmi@yahoo.com>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: Implicit assumptions (was: Re: Some fun with -O2)
Message-ID:  <22093525.sr7ieKrsik@t450s.local.lan>
In-Reply-To: <ABC6559D-66E6-4671-A11A-FA225B55C9B6@yahoo.com>
References:  <mailman.29.1610625600.45116.freebsd-hackers@freebsd.org> <1648904.MsCH1bHPGx@t450s.local.lan> <ABC6559D-66E6-4671-A11A-FA225B55C9B6@yahoo.com>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.

--nextPart3320458.j5jfTYeP2b
Content-Transfer-Encoding: 7Bit
Content-Type: text/plain; charset="us-ascii"

My previous naiive attempt silently assumed CHAR_BIT = 8...  Isn't that funny?  
Now this is hopefully my last try to fix the issue, it should be agnostic to 
any padding and machine word length.  It does not cover *time_t* beeing a 
floating point type, though.  Compile witch 'cc -DDEBUG' to get the 
diagnostics to *stderr* .

Regards

--- check_mktime.c.patch ---
--- check_mktime.c.orig	2021-01-15 03:19:33.962253000 +0100
+++ check_mktime.c	2021-01-17 22:01:38.157124000 +0100
@@ -3,6 +3,11 @@
 # include <sys/time.h>
 # include <time.h>
 # include <unistd.h>
+# include <stdlib.h>	/* putenv(), exit(), EXIT_xxx */
+# include <stdint.h>	/* intmax_t */
+# include <stdio.h>	/* printf() */
+# include <limits.h>	/* CHAR_BIT, INT_MAX */
+# include <inttypes.h>	/* format spec PRIX64: ll/l + X on 32/64-bit arch */
 
 /* Work around redefinition to rpl_putenv by other config tests.  */
 #undef putenv
@@ -16,6 +21,74 @@
 };
 #define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
 
+/* The following two routines (should) work for integer representation
+   in either 2's or 1's complement and for any #bits per byte.  */
+
+/* Count the bits set in any integer type.
+   Returns the precision (width - padding bits - sign bit) iff given
+   the xxx_MAX value of any integer type, signed or unsigned.
+   From SEI CERT C Coding Standard:
+   Rules for Developing Safe, Reliable, and Secure Systems (2016)  */
+size_t
+popcount (num)
+uintmax_t num;
+{
+  size_t cnt = 0;
+    
+  while (num != 0)
+    {
+      if (num % 2 == 1)
+	cnt++;
+      num >>= 1;
+    }
+  return cnt;
+}
+#define PRECISION(max_value)	popcount(max_value)
+#define SIGN_BIT		(1)
+#ifndef MIN
+#define MIN(a, b)		((a) < (b)? (a): (b))
+#endif
+/* Guess the maximum value of a time_t from it's storage width.
+   On error: returns (time_t)(-1) iff time_t is longer than an intmax_t.
+   ASSERTION: time_t is a signed integer type,
+   i.e. not (unsigned, but the bit pattern of (-1) treated special).  */
+time_t
+guess_time_t_max ()
+{
+  time_t t0, t1 = (time_t)(-1);
+  size_t size = sizeof(time_t);
+  size_t prec;
+  uintmax_t max;
+  uint64_t u64;
+
+  if (sizeof(time_t) > sizeof(uintmax_t))
+    return t1;
+  
+  /* Get an uintmax_t with all bits set that could be in a time_t.
+     We can not do this calculation with a time_t as long we do not
+     know its precision (overflow could occur).  */
+  prec = MIN (PRECISION (UINTMAX_MAX), CHAR_BIT * sizeof(time_t));
+  max = (uintmax_t) 1 << (prec - 1);
+  max = max|(max - 1);
+
+  t0 = max;		/* maybe trucation happens here */
+  
+  /* Now account for any padding */
+  prec = PRECISION(t0) - SIGN_BIT;
+  t0 = (time_t) 1 << (prec - 1);
+  t1 = t0|(t0 - 1);
+  
+#ifdef DEBUG
+  fprintf (stderr, "time_t_max\t= 0x%"PRIX64"\n", (u64 = t1));
+  fprintf (stderr, "sizeof(time_t)\t= %2zd byte\n", size);
+  fprintf (stderr, "precision\t= %2zd bit\n", prec);
+  fprintf (stderr, "padding\t\t= %2zd bit\n", CHAR_BIT*size - prec - 
SIGN_BIT);
+  fprintf (stderr, "bits per byte\t= %2d bit\n", CHAR_BIT);
+#endif /* DEBUG */
+  return t1;
+}
+#undef SIGN_BIT
+
 /* Fail if mktime fails to convert a date in the spring-forward gap.
    Based on a problem report from Andreas Jaeger.  */
 static void
@@ -37,8 +110,8 @@
   tm.tm_min = 0;
   tm.tm_sec = 0;
   tm.tm_isdst = -1;
-  if (mktime (&tm) == (time_t)-1)
-    exit (1);
+  if (mktime (&tm) == (time_t)(-1))
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -47,10 +120,10 @@
 {
   struct tm *lt;
   if ((lt = localtime (&now)) && mktime (lt) != now)
-    exit (1);
+    exit (EXIT_FAILURE);
   now = time_t_max - now;
   if ((lt = localtime (&now)) && mktime (lt) != now)
-    exit (1);
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -67,7 +140,7 @@
   tm.tm_isdst = -1;
   mktime (&tm);
   if (tm.tm_mon != 2 || tm.tm_mday != 31)
-    exit (1);
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -82,7 +155,7 @@
   alarm (10);
   now = mktime (&tm);
   alarm (0);
-  if (now != (time_t) -1)
+  if (now != (time_t)(-1))
     {
       struct tm *lt = localtime (&now);
       if (! (lt
@@ -96,7 +169,7 @@
 	     && lt->tm_wday == tm.tm_wday
 	     && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
 		  == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
-	exit (1);
+	exit (EXIT_FAILURE);
     }
 }
 
@@ -106,9 +179,10 @@
   time_t t, delta;
   int i, j;
 
-  for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
-    continue;
-  time_t_max--;
+  time_t_max = guess_time_t_max ();
+  if (time_t_max == (time_t)(-1))
+    exit (EXIT_FAILURE);
+
   delta = time_t_max / 997; /* a suitable prime number */
   for (i = 0; i < N_STRINGS; i++)
     {
@@ -120,11 +194,15 @@
       mktime_test ((time_t) 60 * 60);
       mktime_test ((time_t) 60 * 60 * 24);
 
-      for (j = 1; 0 < j; j *= 2)
+      /* NOTE This does not reach very large values > time_t_max/2
+         neither the (susceptible for overflow) version
+         for (j = 1; 0 < j; j *= 2) does  */
+      for (i = j = 1; i < PRECISION(INT_MAX); i++, j *= 2)
         bigtime_test (j);
       bigtime_test (j - 1);
     }
   irix_6_4_bug ();
   spring_forward_gap ();
-  exit (0);
+  exit (EXIT_SUCCESS);
 }
+/*! vi: set ai tabstop=8 shiftwidth=2: */

-- 
=|o)	"Stell' Dir vor es geht und keiner kriegt's hin." (Wolfgang Neuss)
--nextPart3320458.j5jfTYeP2b
Content-Disposition: attachment; filename="check_mktime.c.patch"
Content-Transfer-Encoding: 7Bit
Content-Type: text/x-patch; charset="UTF-8"; name="check_mktime.c.patch"

--- check_mktime.c.orig	2021-01-15 03:19:33.962253000 +0100
+++ check_mktime.c	2021-01-17 22:01:38.157124000 +0100
@@ -3,6 +3,11 @@
 # include <sys/time.h>
 # include <time.h>
 # include <unistd.h>
+# include <stdlib.h>	/* putenv(), exit(), EXIT_xxx */
+# include <stdint.h>	/* intmax_t */
+# include <stdio.h>	/* printf() */
+# include <limits.h>	/* CHAR_BIT, INT_MAX */
+# include <inttypes.h>	/* format spec PRIX64: ll/l + X on 32/64-bit arch */
 
 /* Work around redefinition to rpl_putenv by other config tests.  */
 #undef putenv
@@ -16,6 +21,74 @@
 };
 #define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
 
+/* The following two routines (should) work for integer representation
+   in either 2's or 1's complement and for any #bits per byte.  */
+
+/* Count the bits set in any integer type.
+   Returns the precision (width - padding bits - sign bit) iff given
+   the xxx_MAX value of any integer type, signed or unsigned.
+   From SEI CERT C Coding Standard:
+   Rules for Developing Safe, Reliable, and Secure Systems (2016)  */
+size_t
+popcount (num)
+uintmax_t num;
+{
+  size_t cnt = 0;
+    
+  while (num != 0)
+    {
+      if (num % 2 == 1)
+	cnt++;
+      num >>= 1;
+    }
+  return cnt;
+}
+#define PRECISION(max_value)	popcount(max_value)
+#define SIGN_BIT		(1)
+#ifndef MIN
+#define MIN(a, b)		((a) < (b)? (a): (b))
+#endif
+/* Guess the maximum value of a time_t from it's storage width.
+   On error: returns (time_t)(-1) iff time_t is longer than an intmax_t.
+   ASSERTION: time_t is a signed integer type,
+   i.e. not (unsigned, but the bit pattern of (-1) treated special).  */
+time_t
+guess_time_t_max ()
+{
+  time_t t0, t1 = (time_t)(-1);
+  size_t size = sizeof(time_t);
+  size_t prec;
+  uintmax_t max;
+  uint64_t u64;
+
+  if (sizeof(time_t) > sizeof(uintmax_t))
+    return t1;
+  
+  /* Get an uintmax_t with all bits set that could be in a time_t.
+     We can not do this calculation with a time_t as long we do not
+     know its precision (overflow could occur).  */
+  prec = MIN (PRECISION (UINTMAX_MAX), CHAR_BIT * sizeof(time_t));
+  max = (uintmax_t) 1 << (prec - 1);
+  max = max|(max - 1);
+
+  t0 = max;		/* maybe trucation happens here */
+  
+  /* Now account for any padding */
+  prec = PRECISION(t0) - SIGN_BIT;
+  t0 = (time_t) 1 << (prec - 1);
+  t1 = t0|(t0 - 1);
+  
+#ifdef DEBUG
+  fprintf (stderr, "time_t_max\t= 0x%"PRIX64"\n", (u64 = t1));
+  fprintf (stderr, "sizeof(time_t)\t= %2zd byte\n", size);
+  fprintf (stderr, "precision\t= %2zd bit\n", prec);
+  fprintf (stderr, "padding\t\t= %2zd bit\n", CHAR_BIT*size - prec - SIGN_BIT);
+  fprintf (stderr, "bits per byte\t= %2d bit\n", CHAR_BIT);
+#endif /* DEBUG */
+  return t1;
+}
+#undef SIGN_BIT
+
 /* Fail if mktime fails to convert a date in the spring-forward gap.
    Based on a problem report from Andreas Jaeger.  */
 static void
@@ -37,8 +110,8 @@
   tm.tm_min = 0;
   tm.tm_sec = 0;
   tm.tm_isdst = -1;
-  if (mktime (&tm) == (time_t)-1)
-    exit (1);
+  if (mktime (&tm) == (time_t)(-1))
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -47,10 +120,10 @@
 {
   struct tm *lt;
   if ((lt = localtime (&now)) && mktime (lt) != now)
-    exit (1);
+    exit (EXIT_FAILURE);
   now = time_t_max - now;
   if ((lt = localtime (&now)) && mktime (lt) != now)
-    exit (1);
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -67,7 +140,7 @@
   tm.tm_isdst = -1;
   mktime (&tm);
   if (tm.tm_mon != 2 || tm.tm_mday != 31)
-    exit (1);
+    exit (EXIT_FAILURE);
 }
 
 static void
@@ -82,7 +155,7 @@
   alarm (10);
   now = mktime (&tm);
   alarm (0);
-  if (now != (time_t) -1)
+  if (now != (time_t)(-1))
     {
       struct tm *lt = localtime (&now);
       if (! (lt
@@ -96,7 +169,7 @@
 	     && lt->tm_wday == tm.tm_wday
 	     && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
 		  == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
-	exit (1);
+	exit (EXIT_FAILURE);
     }
 }
 
@@ -106,9 +179,10 @@
   time_t t, delta;
   int i, j;
 
-  for (time_t_max = 1; 0 < time_t_max; time_t_max *= 2)
-    continue;
-  time_t_max--;
+  time_t_max = guess_time_t_max ();
+  if (time_t_max == (time_t)(-1))
+    exit (EXIT_FAILURE);
+
   delta = time_t_max / 997; /* a suitable prime number */
   for (i = 0; i < N_STRINGS; i++)
     {
@@ -120,11 +194,15 @@
       mktime_test ((time_t) 60 * 60);
       mktime_test ((time_t) 60 * 60 * 24);
 
-      for (j = 1; 0 < j; j *= 2)
+      /* NOTE This does not reach very large values > time_t_max/2
+         neither the (susceptible for overflow) version
+         for (j = 1; 0 < j; j *= 2) does  */
+      for (i = j = 1; i < PRECISION(INT_MAX); i++, j *= 2)
         bigtime_test (j);
       bigtime_test (j - 1);
     }
   irix_6_4_bug ();
   spring_forward_gap ();
-  exit (0);
+  exit (EXIT_SUCCESS);
 }
+/*! vi: set ai tabstop=8 shiftwidth=2: */

--nextPart3320458.j5jfTYeP2b--






Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?22093525.sr7ieKrsik>