diff --git a/CLAUDE.md b/CLAUDE.md index 943024e01..19b6d37f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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*: @@ -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 diff --git a/sys/lib/tests/apeabi-probe.c b/sys/lib/tests/apeabi-probe.c new file mode 100644 index 000000000..708453dc9 --- /dev/null +++ b/sys/lib/tests/apeabi-probe.c @@ -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. + * + * `' is here and `' 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. + * `' and friends are out because they want `bool' as a + * keyword and this file has to compile under both compilers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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; +}