Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ glibc's take `const char *`, so even a `-D` rename collides with the
header -- it renames the SOURCE with `sed` into a temporary instead.
`strtol`/`strtoul` cannot be swept at all here, because kencc's
`long` is 32-bit and the host's is 64.
`apeabi-probe.c` is a PROBE and is never run: it is compiled TWICE,
`pcc -a` and `pcc -J -a`, and the DIFF of the two acid dumps names
every APE struct whose layout `-J` moves. Take it before any
tree-wide rebuild.
`structalign-test.c` measures `pcc -J`, conforming struct layout, and
**needs three runs** -- gcc, then `pcc -DPLAN9` WITHOUT `-J` (which
must fail ~12 checks), then with it. *The middle run is the control*:
Expand Down Expand Up @@ -929,6 +933,56 @@ one run tells the two instruments apart rather than two rounds.
*The predicted failure did not fire and the prediction was written
down; that is the condition doing its job, not the flag.*

**AND `-J` WAS WORKING THE WHOLE TIME -- 14 of 97 -> 7, EXACTLY THE
PREDICTION.** All seven of cfront's OWN types fall into line under
`pcc -J`: `node` 3, **`name` 144**, `basic_inst` 168, `funct_inst`
200, `state` 88, `templ_compilation` 1, `templ_inst` 200. What is
left is the seven glibc types, which `-J` cannot touch because they
describe the machine that GENERATED the C -- and the two `__Q3_`
entries confirm the reading rather than merely vanishing: `_C4` is
**20** (`__clock_t` four times over at 4 bytes) against Linux's 32,
`_C6` is 8 (`long si_band` + `int si_fd`) against 16. *The 32-bit
`long` invariant showing through, not padding.*
**THE "FLAG NOT PICKED UP" MESSAGE WAS THE MARKER, NOT THE FLAG, AND
IT COST THREE REBUILDS.** `pcc` runs **`/bin/cpp` ITSELF** and pipes
it into `cc` (`pcc.c:299`'s `dopipe`), so the source reaches `cc`
**already preprocessed** -- and neither `dodefine()` in `cc`'s own
symbol table nor `cc`'s `defs[]` can define a macro the source will
ever see. *There are THREE preprocessors in play and only the one
`pcc` spawns actually runs*; the first two attempts configured the
other two. **Name the preprocessor that RUNS, not the one with the
right name** -- the shadowed-`config.h` lesson a third time.
**And the two instruments never disagreed.** `structalign-test` was
compiled **with** `-J` and `cfrontsz-probe` **without** it, and I
read them as the same compile for two rounds. Everything else was
cleared locally first: preprocessing the probe's own translation unit
with cfront's exact flags gives ONE `struct node`, `TOK` and `bit`
both `unsigned char`, and gcc computing 3; `cfront_translated.h`
defines none of the seven; `-B` touches only undeclared-function
diagnostics. *Two compiles differing in one flag is not two compilers
disagreeing, and the flag was in the command line both times.*

**THE OBVIOUS WAY TO TURN `-J` ON TREE-WIDE DOES NOT WORK, and that
is the finding to carry.** `-J` in `sys/src/ape/config`'s CFLAGS
reaches **32 of 137** mkfiles -- the other **105 ASSIGN `CFLAGS=`**
rather than appending `$CFLAGS`, and `cmd/cfront/mkfile:52` is one of
them. `CC` is no better: **59** mkfiles reassign it. *Both of the two
variables a build system offers for exactly this have holes, and
either would have produced the silent ABI split rather than a clean
change* -- 32 packages conforming, 105 not, every symbol still
resolving. **The only mechanism that reaches every APE compile is
`pcc` itself**, since all 59 reassignments still name `pcc`; native
`6c` builds stay on the 9front rule, which is what `cmd2/vts` and
`vtwin` need when they link the host's own `libc.a`.
**`apeabi-probe.c` is what to run BEFORE deciding that**: two
compiles and a diff, `pcc -a` against `pcc -J -a`. `-a` emits acid
definitions carrying every struct's size and every member's offset
(it is `mkone`'s own `%.acid` rule), so the diff is a complete list,
**from the compiler itself**, of everything `-J` moves inside the APE
world. An empty diff would mean the flag is free; a long one is the
cost named struct by struct before a single object is rebuilt. 0
errors against the staged headers on the host.

**AND THE cfront SIZE PROBE WAS `.gitignore'd, SO THE VM NEVER GOT
IT.** `pcc ... cfrontsz-probe.c` answered `Can't open input file` and
`ls cfront*.c` showed only the two stub files. **git is the only
Expand Down
143 changes: 143 additions & 0 deletions sys/lib/tests/apeabi-probe.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* apeabi-probe -- which APE structs does `-J' MOVE?
*
* TWO COMPILES AND A DIFF, on the VM:
*
* pcc -a apeabi-probe.c > /tmp/abi-old.acid
* pcc -J -a apeabi-probe.c > /tmp/abi-new.acid
* diff /tmp/abi-old.acid /tmp/abi-new.acid
*
* `-a' makes the compiler emit acid definitions, which carry each
* struct's SIZE and every member's OFFSET -- it is what `mkone's
* `%.acid' rule uses. So the diff is a complete list, from the
* compiler itself, of everything whose layout `-J' changes inside the
* APE world. **An empty diff would mean -J is free; a long one is the
* cost, named struct by struct, before a single object is rebuilt.**
*
* ------------------------------------------------------------------
* WHY THIS HAS TO BE MEASURED BEFORE THE TREE-WIDE BUILD.
*
* `-J' is all-or-nothing for a linked world: it moves `FILE',
* `struct stat' and `DIR' as readily as anything else, and a package
* built with it against a libap built without it disagrees about them
* **silently, with no link error, because every symbol still
* resolves**. The failure arrives as a wrong field, later, somewhere
* else.
*
* AND THE OBVIOUS WAY TO TURN IT ON DOES NOT WORK. Putting `-J' in
* `sys/src/ape/config's CFLAGS reaches **32 of 137** mkfiles: the
* other 105 ASSIGN `CFLAGS=' outright rather than appending
* `$CFLAGS', and `cmd/cfront/mkfile:52' is one of them. Putting it on
* `CC' is no better -- 59 mkfiles reassign that too. *The two
* variables a build system offers for exactly this both have holes,
* and either one would have produced the silent split rather than a
* clean change.*
*
* So the mechanism has to be `pcc' itself, which every APE compile
* runs however the mkfile spells `$CC' -- and that is a decision to
* take with this diff in hand, not before it.
*
* ------------------------------------------------------------------
* WHAT IS INCLUDED AND WHAT IS LEFT OUT.
*
* The ABI-bearing headers: the types that cross a library boundary,
* get embedded in another struct, or are written to disk. Headers
* that only declare functions add nothing to an acid dump.
*
* `<regex.h>' is here and `<pcre2posix.h>' is NOT, deliberately: they
* define `regex_t' and `regmatch_t' as ALTERNATIVES and no program
* includes both, which `apehdr-sweep's together case already records.
* `<curses.h>' and friends are out because they want `bool' as a
* keyword and this file has to compile under both compilers.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <locale.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <utime.h>
#include <wchar.h>
#include <wctype.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <termios.h>
#include <pthread.h>

/*
* The acid dump only describes types the translation unit actually
* USES, so each one is named by an object. A pointer would not do it:
* the compiler emits a definition for a struct it has laid out, and
* laying it out is what declaring an instance forces.
*/
struct stat _abi_stat;
struct dirent _abi_dirent;
struct tm _abi_tm;
struct timeval _abi_timeval;
struct timespec _abi_timespec;
struct tms _abi_tms;
struct rusage _abi_rusage;
struct utsname _abi_utsname;
struct passwd _abi_passwd;
struct group _abi_group;
struct utimbuf _abi_utimbuf;
struct sockaddr _abi_sockaddr;
struct sockaddr_in _abi_sockaddr_in;
struct sockaddr_in6 _abi_sockaddr_in6;
struct in_addr _abi_in_addr;
struct in6_addr _abi_in6_addr;
struct ip_mreq _abi_ip_mreq;
struct hostent _abi_hostent;
struct servent _abi_servent;
struct protoent _abi_protoent;
struct addrinfo _abi_addrinfo;
struct termios _abi_termios;
struct sigaction _abi_sigaction;
struct lconv _abi_lconv;
struct div_t_holder { div_t d; ldiv_t l; lldiv_t ll; };
struct div_t_holder _abi_divs;
FILE *_abi_filep;
FILE _abi_file;
DIR *_abi_dirp;
jmp_buf _abi_jmpbuf;
sigjmp_buf _abi_sigjmpbuf;
fd_set _abi_fdset;
regex_t _abi_regex;
regmatch_t _abi_regmatch;
mbstate_t _abi_mbstate;
pthread_t _abi_pthread;
pthread_attr_t _abi_pthread_attr;
pthread_mutex_t _abi_pthread_mutex;
pthread_cond_t _abi_pthread_cond;
pthread_rwlock_t _abi_pthread_rwlock;
sigset_t _abi_sigset;

int
main(void)
{
/*
* Nothing is printed. The whole output is the acid dump that
* `-a' writes to stdout, so this program is never run -- only
* compiled, twice.
*/
return 0;
}
Loading