commit 27daf68ca620a6ad909d6abe17c49cf8e2468e0a
parent 5b5ed0309cc714b0e05fd444067adafc6265d892
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 29 Apr 2026 16:31:32 -0700
tests/cc: tentative bss sizing
Diffstat:
2 files changed, 77 insertions(+), 0 deletions(-)
diff --git a/tests/cc/132-tentative-bss-sizing.c b/tests/cc/132-tentative-bss-sizing.c
@@ -0,0 +1,76 @@
+/* C 6.9.2 — tentative defs reserve sizeof(T) bytes of zero-init
+ * storage. Companion to 124-tentative-static.c, which only exercised
+ * `int`. tcc-boot2 needs the wide-pointer / array cases too:
+ * mes/globals.c declares `char **environ;`, `int errno;`, etc.
+ * cc.scm has been emitting too few or too many bytes for these
+ * (see :environ → 1 byte, :errno → 16 bytes in libc.P1pp).
+ *
+ * Each global is sized differently and probed via address-of so the
+ * compiler can't fold the read. Wrong sizing manifests as either:
+ * - reads past the slot return 0xff... or stale data → mismatch
+ * - 8-byte stores to a 1-byte slot scribble adjacent globals
+ * (so writing g_pp = X then reading g_int near it shows X
+ * bleeding through). */
+
+extern unsigned long strlen (const char *);
+extern long sys_write (long fd, long buf, long len);
+
+char g_char; /* 1 byte */
+short g_short; /* 2 bytes */
+int g_int; /* 4 bytes */
+long g_long; /* 8 bytes */
+char *g_p; /* 8 bytes */
+char **g_pp; /* 8 bytes — the environ shape */
+int g_arr[4]; /* 16 bytes */
+char *g_parr[3]; /* 24 bytes */
+
+static long
+fail (const char *msg)
+{
+ sys_write (2, (long) msg, (long) strlen (msg));
+ return 1;
+}
+
+int
+main (void)
+{
+ /* Tentative defs must zero-init. */
+ if (g_char != 0) return fail ("g_char not zero\n");
+ if (g_short != 0) return fail ("g_short not zero\n");
+ if (g_int != 0) return fail ("g_int not zero\n");
+ if (g_long != 0) return fail ("g_long not zero\n");
+ if (g_p != 0) return fail ("g_p not zero\n");
+ if (g_pp != 0) return fail ("g_pp not zero\n");
+ if (g_arr[0] || g_arr[1] || g_arr[2] || g_arr[3])
+ return fail ("g_arr not zero\n");
+ if (g_parr[0] || g_parr[1] || g_parr[2])
+ return fail ("g_parr not zero\n");
+
+ /* Each slot must be addressable for its full sizeof(T).
+ * Write a sentinel through the address, read it back. If the
+ * slot is undersized, the high bytes spill into the next global
+ * and the read returns a truncated value. */
+ long *pl = &g_long;
+ *pl = 0x1122334455667788L;
+ if (g_long != 0x1122334455667788L)
+ return fail ("g_long roundtrip\n");
+
+ g_pp = (char **) 0xdeadbeef00112233L;
+ if ((long) g_pp != (long) 0xdeadbeef00112233L)
+ return fail ("g_pp roundtrip\n");
+
+ /* Array element: write the last slot — only valid if the full
+ * 16-byte / 24-byte allocation actually exists. */
+ g_arr[3] = 42;
+ if (g_arr[3] != 42) return fail ("g_arr[3] roundtrip\n");
+ g_parr[2] = (char *) 0x99;
+ if ((long) g_parr[2] != 0x99) return fail ("g_parr[2] roundtrip\n");
+
+ /* And no spill: writing g_pp must NOT change g_int next door.
+ * (Slot order in memory follows declaration order in the TU.) */
+ g_int = 0;
+ g_pp = (char **) 0xfffffffffffffffeL;
+ if (g_int != 0) return fail ("g_pp store spilled into g_int\n");
+
+ return 0;
+}
diff --git a/tests/cc/132-tentative-bss-sizing.expected-exit b/tests/cc/132-tentative-bss-sizing.expected-exit
@@ -0,0 +1 @@
+0