Skip to content

Claude/upgrade ape c library mm z gd - #612

Merged
staalmannen merged 2 commits into
mainfrom
claude/upgrade-ape-c-library-mmZGd
Oct 8, 2026
Merged

staalmannen merged 2 commits into
mainfrom
claude/upgrade-ape-c-library-mmZGd

Conversation

@staalmannen

Copy link
Copy Markdown
Owner

No description provided.

claude added 2 commits October 8, 2026 03:34
The mkfile left `LIB=' as a question on the grounds that `src' holds
145,752 distinct `_Z'-prefixed tokens, so a grep over them measures
nothing.  True of a grep; not true of the linker's own answer, which
can be had on the host: compile all 133 files with gcc, take `nm -u'
over src/'s 75 objects, and subtract what those and lib_src/'s 51
define.

  src/ externals                               629
    satisfied by libedg.a                        1   _ZnwyPv
    plain C names, all present in libap         70
    gcc artefacts, absent under pcc              2
    template instantiations, defined nowhere   556

The one is `_ZnwyPv' -- operator new(unsigned long long, void *),
placement new -- from lib_src/placenew.c.  So the line is needed, for
one small member, and it is another reason lib/edg goes first: in
mkone `$LIB' is a prerequisite of `$O.out' handed straight to `$LD',
the dependency that had bacon unbuildable until lua was.

The caveat is stated rather than left out: this measures the NAMES in
the C, which pcc and gcc must agree on because the C is the same text.
It cannot speak for whether 6l reports the same SET, since an archive
is pulled member by member and the reachability is the linker's.

PREDICTION FOR THE FIRST LINK, written before it is run: 6l reports
undefined `_ZN3edg...I...E' names and nothing else, around 556 of
them, every one a template instantiation no translation unit emitted.
Zero vtables, zero typeinfo, zero guard variables, zero thunks --
those are all on the duplicate side (3,124 symbols defined in more
than one object).  If 6l names a plain C function or a vtable instead,
the instantiation reading is wrong and the NOTE needs re-reading
rather than this mkfile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs
All 75 of EDG's src/ files compile, sys_predef.c's 315,616 lines
included.  The link is where it stopped, with `redefinition:' until
`too many errors'.

MY PREDICTION WAS WRONG AND IT WAS ABOUT ORDERING.  I predicted 6l
would report undefined template instantiations; it reports
redefinitions, because 6l catches duplicates as it LOADS each object
and only reports undefined symbols at the end -- so it never reached
the pass the prediction was about.  What it named confirms the host
sweep symbol for symbol: edg::max_val<unsigned long long> and
edg::skip_typerefs, both measured at 41 copies, and
Ptr_map<...>::get_with_hash at 39.

THE FIX WAS ALREADY IN THE LINKER.  6l/obj.c:996 is
`if(p->from.scale & DUPOK){ skip = 1; goto casdef; }' on a duplicate
ATEXT, and 6l/asm.c:548 uses sym->dupok to suppress `multiple
initialization' for duplicate DATA.  DUPOK is (1<<1) in eight *.out.h.
The one missing piece was a way for C to set the bit: gpseudo() wrote
`p->from.scale = (profileflg ? 0 : NOPROF)' and nothing ever OR'd into
it.  The sixth time this tree has found a working implementation of
the thing it could not do sitting next to the thing that could not.

-C marks every TEXT and GLOBL in the translation unit dupok.  It is
blunt, and cc.h says so: it marks everything rather than the
vague-linkage entities, so two genuinely different functions of one
name become first-wins instead of a diagnostic.  That is safe for
generated C, where every duplicate is the same entity by construction,
and it is not safe as a default -- only sys/src/ape/cmd/edg passes it.
The precise version is teaching cc __attribute__((__weak__)), which
EDG already emits (26,712 of them) when its target is gcc, and that
needs a re-translation.

x86 AND RISC USE DIFFERENT FIELDS, which one uniform patch would have
got wrong.  6c/8c carry TEXT flags in p->from.scale; 5c/7c/kc/qc/vc
carry them in p->reg and assign it only for ATEXT, so AGLOBL had to be
named explicitly.  zprog.reg is NREG rather than 0, so every RISC
AGLOBL already reaches its linker carrying NREG in the flags field --
whether that was accidentally dupok was checked, not assumed (16 and
32 against 1<<1: clear).  ADATA is excluded everywhere; on x86 it is
harmless anyway because every ADATA caller in swt.c overwrites
from.scale with the datum's width.

FOUR TARGETS CANNOT HONOUR IT AND NOW SAY SO.  9l DECLARES dupok in
l.h and never reads it -- zero hits in its .c where every other linker
has two -- a pre-existing gap found by this change and recorded, not
fixed, since nothing here builds power64 and an untested linker change
is worse than a named limit.  1l/2l have no such field at all.  On
those three backends gpseudo diagnoses once rather than setting a bit
the linker will ignore, because a flag that silently does nothing
produces a link failure that looks like a different bug.

pcc names -C explicitly: its ARGBEGIN has no default arm, so a flag it
does not know is dropped and the build looks exactly right while the
bit is never set.  That is the trap -J paid three rebuilds for.  -C
also predefines __APEXP_DUPOK__, through defs[] as well as dodefine(),
because pcc spawns /bin/cpp itself and a macro cc defines internally
reaches nothing.

NOT COMPILED ANYWHERE: kencc's sources need Plan 9's <u.h> and
<libc.h>, so the first build is the VM's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs
@staalmannen
staalmannen merged commit fd39894 into main Oct 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants