From 62bf2e7f7a922218f9051adfffe43864c1b59122 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 16:52:36 +0000 Subject: [PATCH 1/4] edg: cpfe runs on 9front; record the first-translation command After the _main rename cpfe reaches its own command-line parser and answers with its own diagnostics instead of faulting at pc=argc. The entry-point diagnosis is confirmed behaviourally. Both messages in that run are cpfe working rather than refusing: --help and -V are eccp DRIVER options, cpfe's own table in src/cmd_line.c has no `help' entry at all and spells the other --version, and -V reaching "missing source file name" means it was accepted with only the operand absent. --no_standard_includes is the driver's too, which matters for a hand-written command: it is not in cpfe's table because the driver implements it by not passing its default --sys_include list. cpfe has no standard include path of its own, so there is nothing to suppress. The NOTE now carries the first-translation command. The half that matters is pcc -c on the generated C: cpfe emitting C says the front end runs, pcc accepting it says kencc_targ.h describes this machine. Use a toy with no `new' and no static constructors -- those are the two places the runtime is reached and the static-init gap is open. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 19 ++++++++++++++++++- sys/src/external/edg/NOTE | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index fbfda92a7..92709b028 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2131,7 +2131,24 @@ live either way. working precedent in the directory to copy. *The rule was already written down here, in its `awk -v name=$name` spelling, and a rule recorded for one program's arguments did not transfer to a - compiler's.* + compiler's. + **CONFIRMED: cpfe RUNS.** It reaches its own command-line parser + and answers with its own diagnostics -- `invalid option: --help`, + and `-V` giving `missing source file name`. *That is the + entry-point diagnosis confirmed behaviourally*, and **both + messages are cpfe working rather than refusing**: `--help` and + `-V` are **`eccp` DRIVER** options, cpfe's own table has no + `help` entry at all and spells the other `--version`, and `-V` + reaching "missing source file name" means it was ACCEPTED with + only the operand absent. **`--no_standard_includes` is the + driver's too** and must not go in a hand-written command: cpfe + has no standard include path of its own, so there is nothing to + suppress. The first translation command is in the NOTE; the half + that matters is **`pcc -c` on the generated C**, since cpfe + emitting C says the front end runs while pcc accepting it says + `kencc_targ.h` describes this machine. Use a toy with **no `new` + and no static constructors** -- those are the two places the + runtime is reached and the static-init gap above is open.* - **AND THE RUNTIME ADDS NOTHING libap ALREADY PROVIDES, checked rather than assumed.** `lib_src` holds `exit.c`, `main.c`, `error.c` and `memzero.c`, which is the shape that cost this tree diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index 9a949c06f..af6a64fd0 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -698,3 +698,43 @@ mkfile. Quoted. Every other `-D' in the tree arrives through an expanded `$CFLAGS', where mk substitutes after rc has parsed, so this is the first LITERAL one here and had no working precedent in the directory to copy from. + +CONFIRMED: cpfe RUNS ON 9front. After the rename it reaches its own +command-line parser and answers with its own diagnostics: + + $ cpfe --help + Command-line error: invalid option: --help + 1 catastrophic error detected in this compilation. + $ cpfe -V + Command-line error: missing source file name + +THAT IS THE ENTRY-POINT DIAGNOSIS CONFIRMED BEHAVIOURALLY, and both +messages are cpfe working rather than refusing. `--help' and `-V' are +`eccp' DRIVER options; cpfe's own table (src/cmd_line.c) has no `help' +entry at all and spells the other one `--version'. `-V' reaching +"missing source file name" means it was ACCEPTED and only the operand +was absent. + +AND `--no_standard_includes' IS THE DRIVER'S TOO, which matters for +anyone writing the first command by hand: it is not in cpfe's option +table, because the driver implements it by simply not passing its +default --sys_include list. cpfe has no standard include path of its +own -- it searches exactly what it is given -- so there is nothing to +suppress. + +THE FIRST TRANSLATION ON 9front, as a command (from bash inside +apexp-sh; the `=' needs no quoting there, only in an mkfile recipe, +where rc parses it): + + cpfe --c++14 --no_exceptions --no_rtti \ + --sys_include=/include_c++ \ + --sys_include=/include_c99 \ + --sys_include=/amd64/include/ape \ + --sys_include=/sys/include/ape \ + --gen_c_file_name=/tmp/t.c /tmp/t.cpp + +Start with a toy that uses NO `new' and no static constructors: those +are the two places the runtime is reached, and the static-init gap +above is open. `pcc -c /tmp/t.c' is the half that matters -- cpfe +emitting C says the front end runs, and pcc accepting it says the +target configuration in kencc_targ.h describes this machine. From c76af9c7922922d74a35dc11356c2978f4ebb8d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:10:35 +0000 Subject: [PATCH 2/4] edg: record the C++ standard library position for future work EDG ships no standard library and this directory contains none. include_c++ holds eleven LANGUAGE-SUPPORT headers -- its own README says "only those headers that require specific magic to work right with the EDG front end" -- and a grep across all of them for basic_string|vector|iostream|ostream is empty. lib_src (51 files, 7191 lines) is the matching runtime: new/delete, vtables, RTTI, static init, pure-virtual. That is the libsupc++/libc++abi layer, not libstdc++. What works with no library at all is the subset EDG itself is written in, which is why the self-translation worked. The finding that decides the rest: EDG's exception handling here is setjmp/longjmp with its own region bookkeeping -- __eh_curr_region 138 occurrences, an_eh_stack_entry 99, setjmp 19, longjmp 2, and zero references to _Unwind_ anything across all 51 files. So exceptions need no libgcc, no libunwind, no .eh_frame and no personality routine, which is the only model that could work on a target with none of those; but libc++'s EH integration assumes the Itanium ABI, so the libsupc++-shaped runtime is not the conforming ABI layer it looks like. Only the grep says so. Not an obstacle, checked: the type-trait builtins a modern STL needs are in cpfe's table. And the axis that decides a candidate is the NAMESPACE rather than maintenance status: APExp exists to build existing software with minimal modification, and code written against std::vector does not compile against etl:: or eastl:: however good those libraries are. A renamed STL serves new code; only a std:: one serves a port. The NOTE carries the candidate survey (STLport, ETL, EASTL, uClibc++, libstdc++), an honest ordering, and a provenance paragraph marking which lines are measured in-tree and which are only recalled or came from one web search -- the EASTL line in particular was NOT confirmed and is flagged as unchecked. Nothing is started and none of it blocks anything already working. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 35 ++++++++++++ sys/src/external/edg/NOTE | 115 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 92709b028..45e85a697 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2149,6 +2149,41 @@ live either way. `kencc_targ.h` describes this machine. Use a toy with **no `new` and no static constructors** -- those are the two places the runtime is reached and the static-init gap above is open.* + - **THERE IS NO C++ STANDARD LIBRARY HERE AND EDG SHIPS NONE, and + the EXCEPTION MODEL decides what could fill the gap.** `lib_src` + is the **libsupc++/libc++abi layer** -- new/delete, vtables, RTTI, + static init, pure-virtual -- and `include_c++`'s eleven headers + are the LANGUAGE-SUPPORT set, its own README saying *"only those + headers that require specific magic"*. A grep for + `basic_string|vector|iostream` across all of them is **empty**. + **What works with no library at all is the subset EDG ITSELF is + written in** -- classes, templates, virtual dispatch, RAII, + `new`/`delete`, plus `bad_alloc`/`type_info`/`initializer_list` -- + which is why the self-translation worked. + **AND THE LAYERING LOOKS STANDARD WHILE THE EH MODEL IS NOT.** + EDG's exceptions are **setjmp/longjmp with its own regions** + (`__eh_curr_region` 138, `an_eh_stack_entry` 99, `setjmp` 19) and + there are **ZERO `_Unwind_` references in all 51 files**. *Good*: + no libgcc, no libunwind, no `.eh_frame`, no personality routine -- + the only model that could work on a target with none of those, and + the reason EDG's C back end exists. *Bad*: **libc++'s EH assumes + the ITANIUM ABI**, so this is not the usual drop-libc++-onto-a- + conforming-ABI arrangement the libsupc++ shape suggests. Only the + grep says so. The type-trait builtins a modern STL needs ARE in + cpfe's table, checked (`__is_trivially_copyable`, + `__is_constructible`, `__is_base_of`, `__underlying_type`, ...). + **AND THE AXIS THAT DECIDES A CANDIDATE IS THE NAMESPACE, NOT + MAINTENANCE**: APExp exists to build existing software with + minimal modification, and code written against `std::vector` does + not compile against `etl::` or `eastl::` however good they are. + *A renamed STL serves NEW code; only a `std::` one serves a PORT.* + STLport fits this compiler best and died in 2008; ETL is + maintained but embedded and fixed-capacity; EASTL is more + complete; **uClibc++ is the one reported to be `std::` with + iostreams**, and is inactive at 0.2.5. Ordering, candidates and + **a provenance paragraph saying which lines are measured and + which are only recalled or searched** are in the NOTE. *Nothing + started; none of it blocks anything already working.* - **AND THE RUNTIME ADDS NOTHING libap ALREADY PROVIDES, checked rather than assumed.** `lib_src` holds `exit.c`, `main.c`, `error.c` and `memzero.c`, which is the shape that cost this tree diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index af6a64fd0..fae3fb46e 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -738,3 +738,118 @@ are the two places the runtime is reached, and the static-init gap above is open. `pcc -c /tmp/t.c' is the half that matters -- cpfe emitting C says the front end runs, and pcc accepting it says the target configuration in kencc_targ.h describes this machine. + + +THE C++ STANDARD LIBRARY: WHAT EDG GIVES AND WHAT IT DOES NOT +------------------------------------------------------------- + +Asked as soon as cpfe ran, and worth settling before anyone starts: no, +EDG ships no standard library, and this directory contains none. + +WHAT IS HERE, MEASURED. `include_c++' holds ELEVEN headers and its own +README says exactly why: "only those headers that require specific +magic to work right with the EDG front end." They are the +LANGUAGE-SUPPORT set -- , , , , +, , , , +, plus cxxabi.h and edg_internal_defines. A grep across all +of them for `basic_string|vector|iostream|ostream' is EMPTY. +`include_c99' holds one file, `iso646.h'. + +`lib_src' (51 files, 7191 lines) is the matching RUNTIME: the operator +new/delete families, vtables, RTTI, static initialisation, +pure-virtual, exception support. **That is the libsupc++ / libc++abi +layer, not libstdc++.** The distinction is the whole answer: EDG +supplies the compiler and the ABI runtime, and the standard library +proper is absent. + +WHAT WORKS WITH NO LIBRARY AT ALL is more than it sounds, and the +evidence is this directory: classes, templates, inheritance, virtual +dispatch, operator overloading, RAII, new/delete, references, +namespaces, plus std::bad_alloc, type_info and initializer_list from +the headers above. **That is the subset EDG ITSELF is written in**, +which is why the self-translation worked at all -- EDG has never used a +standard library, and `basic_hdrs.h' includes only EDG's own headers. + +AND THE EXCEPTION MODEL IS THE FINDING THAT DECIDES THE REST. +EDG's EH here is **setjmp/longjmp with its own region bookkeeping** -- +`__eh_curr_region' 138 occurrences, `an_eh_stack_entry' 99, `setjmp' +19, `longjmp' 2, and **ZERO references to `_Unwind_' anything** across +all 51 files. + - GOOD: exceptions need no libgcc, no libunwind, no `.eh_frame' and + no personality routine. On a target that has none of those this is + the only model that could work, and it is why EDG's C back end + exists at all. + - BAD FOR libc++: libc++'s EH integration assumes the ITANIUM ABI. + So this is NOT the usual "drop libc++ onto a conforming ABI layer" + arrangement that the libsupc++-shaped `lib_src' makes it look like + -- the two EH halves genuinely disagree. `-fno-exceptions' would + sidestep it, at the price of a standard library that cannot throw. +*The layering LOOKS standard and the EH model is not, and only the +grep says so.* + +NOT AN OBSTACLE, checked rather than assumed: the type-trait builtins a +modern STL needs are in cpfe's table -- `__is_trivially_copyable', +`__is_trivially_destructible', `__is_constructible', `__is_base_of', +`__underlying_type', `__is_aggregate', `__has_trivial_destructor' and +`__builtin_va_arg' all resolve. + +THE CANDIDATES, AND THE AXIS THAT MATTERS IS NOT MAINTENANCE +------------------------------------------------------------ +**APExp's stated goal is building existing UNIX software with minimal +modification.** Against that goal the decisive property is the +NAMESPACE, not how alive the project is: software written against +`std::vector' and `std::string' does not compile against `etl::' or +`eastl::' however good those libraries are. A renamed STL serves NEW +code written for it; only a `std::' implementation serves a PORT. +*That is a reasoning point and holds regardless of which project is +picked.* + + STLport -- designed for weak compilers, EDG-based ones named + among them, which is the one axis where it is the + best fit here. ABANDONED since 2008. + ETL -- actively maintained; EMBEDDED target, and its + containers are fixed-capacity (`etl::vector'), + so it is a different API rather than a thin + substitute. Own namespace. + EASTL -- more complete on containers and algorithms than ETL. + Own namespace; iostreams believed absent. + uClibc++ -- the one candidate reported to use the `std::' + NAMESPACE and to carry iostreams, LGPL, aimed at + small systems and at compilers without full template + support -- which describes this target. Open Hub + marks it INACTIVE at 0.2.5, and a 2009 Gentoo + developer post calls it broken and a source of + security problems. + libstdc++ -- GCC-specific, built on GCC internals. Worse than + any of the above here. + +**PROVENANCE, because it decides how much weight each line carries.** +Everything above the candidate list is MEASURED in this tree by grep +and is reliable. The candidate list is NOT: STLport's 2008 abandonment +and ETL's embedded fixed-capacity design are recalled and were not +re-verified; uClibc++'s status, licence and the 2009 criticism come +from one web search; and EASTL's namespace and missing iostreams were +NOT confirmed by search -- the result said "from general knowledge, not +from these results", so treat that line as unchecked. *Anyone acting +on this list should re-check the project they choose rather than trust +this paragraph*, which is the same rule this tree applies to a stale +suite log. + +THE HONEST ORDERING +------------------- + 1. NOTHING. Treat APExp C++ as "C with classes and templates". + Already reachable, needs no new code, and is strictly more than + cfront ever delivered here. Everything cpfe itself is written in + works today. + 2. A SMALL STL in its own namespace (EASTL or ETL). Buys containers + and algorithms for NEW code; buys nothing for porting existing + software, which is the project's stated purpose. + 3. A `std::'-namespace library (uClibc++, or STLport despite its + age). The only option that helps a PORT, and the one whose + candidates are in the worst condition. + 4. libc++. Large, C++20-era, wants , threading and locale, + and the EH model above is a real obstacle rather than a detail. + +*Nothing here is started and none of it is a prerequisite for anything +already working.* The next EDG step is still the one in the section +above: cpfe translating a toy and `pcc -c' accepting the C. From a37583f49ef0fd858c27f2b1c0031d5ec89b07ce Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:37:27 +0000 Subject: [PATCH 3/4] edg: cpfe's target is not kencc's; kencc_targ.h never reached target.c `check_target_config: must use SoftFloat library' is cpfe refusing its own configuration, not a fault in the input. target.c:10642 fires when targ_ldbl_mant_dig == 64 -- an 80-bit long double it cannot constant- fold without a SoftFloat library it was not built with. cpfe is multi-target: target.c:6525 holds eight configurations, fe_init.c:49180 defaults the index to 0, --target selects by name, and entry 0 (labelled linux_x86_64) is set_legacy_target_config, built from the unsuffixed TARG_* macros that kencc_targ.h overrides. Measured from the generated C, that entry carries the host's values: long 8, long double 16, mant_dig 64. So kencc_targ.h did not reach the translation of target.c. That is a different macro environment from the one already measured: the __weak__ suppression (38,245 -> 0) is a property of the cpfe build, while the TARG_* widths are read when target.c is translated, and only the second bakes into the committed C. Which mechanism -- absent from the translation command, or overridden by cmake_defines.h after it -- is not settled and is recorded as unsettled. Both configurations are in the generated source, so their diff is a complete list rather than a reading: 126 targ_* assignments each, 36 differ. win64 is kencc's integer and float model exactly -- long 4, long double 8/53, size_t/ssize_t/ptrdiff_t as long long -- and also says targ_setjmp_func "setjmp" where legacy says "_setjmp", which is the exact undefined symbol the first link reported. The windows baggage is four families and one stray, all named: wchar_t/wint_t unsigned short (APE says unsigned int); five field-alignment variables, double_field_alignment 4 against kencc's 8 under -J, which is the most likely to bite and is unmeasured; three bit-field rules; pointer-to-member 4 bytes with a short delta; and packing_applies_to_base_classes. Both targ_microsoft_* switches are 0 and the configuration sets no mode flags at all, so --microsoft is a separate option that --target win64 does not imply. jmp_buf differs too (39 ints against 25 longs, APE's being neither), which matters because EDG's exception model is setjmp-based. With the target set, cpfe next wants lib_win64/predefined_macros.txt, which we ship for no target. --clear_flag=use_predefined_macro_file is not a workaround: that file is where a win64 build's _WIN32 and _MSC_VER come from, so suppressing it is precisely LLP64 without the Windows macros, and it is the flag this tree's own translation recipe already uses. --target win64 is a bridge to a first translation with four named divergences. The end state is entry 0 carrying kencc's own values, which fixes every item on that list at once. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 49 +++++++++++++++ sys/src/external/edg/NOTE | 127 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 45e85a697..706114102 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2149,6 +2149,55 @@ live either way. `kencc_targ.h` describes this machine. Use a toy with **no `new` and no static constructors** -- those are the two places the runtime is reached and the static-init gap above is open.* + - **AND cpfe'S TARGET IS NOT kencc'S -- `kencc_targ.h` NEVER REACHED + `target.c`.** The first translation attempt answered + `check_target_config: must use SoftFloat library`, which is cpfe + refusing **its own configuration**: `target.c:10642` fires when + `targ_ldbl_mant_dig == 64`, an 80-bit long double it cannot fold + without a SoftFloat library it was not built with. **cpfe is + MULTI-TARGET** -- `target.c:6525` holds EIGHT configurations, + `fe_init.c:49180` defaults the index to **0**, and entry 0 + (labelled `linux_x86_64`) is `set_legacy_target_config`, built + from the UNSUFFIXED `TARG_*` macros `kencc_targ.h` overrides. + **It carries the HOST's values**: `long 8`, `long double 16`, + `mant_dig 64`. *The `__weak__` suppression (38,245 -> 0) is a + property of the cpfe BUILD; the `TARG_*` widths are read when + `target.c` is TRANSLATED, and only the second bakes into the + committed C.* **Two different macro environments, treated as + one.** Which mechanism -- absent from the translation command, or + overridden by `cmake_defines.h` after it -- is NOT settled. + **AND `--target win64` IS kencc's MODEL EXACTLY, with the cost + enumerated rather than feared.** Both configurations are in the + generated source, so their diff is COMPLETE (the `apeabi-probe` + idiom): **126 `targ_*` assignments, 36 differ.** win64 gets + right what legacy got wrong -- `long 4`, `long double 8`/53, + `size_t`/`ssize_t`/`ptrdiff_t` as `long long`, and + **`targ_setjmp_func "setjmp"`**, which is the exact undefined + symbol the link reported and the NOTE called "a missed line in + `kencc_targ.h`". *A diff reproducing, from a different + direction, a bug found at the LINK is what says it measures the + right thing.* The windows baggage is **four families and one + stray**: `wchar_t`/`wint_t` unsigned SHORT (APE says unsigned + int); five FIELD-ALIGNMENT variables (`double_field_alignment` 4 + against kencc's 8 under `-J` -- *the one most likely to bite, + unmeasured*); three bit-field rules; pointer-to-member 4 bytes + with a `short` delta; and `packing_applies_to_base_classes`. + **Both `targ_microsoft_*` switches are 0 and the config sets no + mode flags at all** -- `--microsoft` is a separate option that + `--target win64` does not imply. Watch `jmp_buf` (39 ints + against legacy's 25 longs, APE's being neither), which matters + here because **EDG's EH is setjmp-based**. + **AND THE PREDEFINED MACRO FILE IS WHERE THE REST OF THE + WINDOWS-NESS LIVES**: with the target set, cpfe next wants + `lib_win64/predefined_macros.txt`, which we ship for no target. + **`--clear_flag=use_predefined_macro_file` is not a workaround** + -- that file is where a win64 build's `_WIN32` and `_MSC_VER` + come from, so suppressing it is precisely *LLP64 without the + Windows macros*, and it is the flag this tree's own translation + recipe already uses. **`--target win64` is a BRIDGE**; the end + state is entry 0 carrying kencc's values, and *every item on the + divergence list is a symptom of the same miss*. Command and full + diff in the NOTE. - **THERE IS NO C++ STANDARD LIBRARY HERE AND EDG SHIPS NONE, and the EXCEPTION MODEL decides what could fill the gap.** `lib_src` is the **libsupc++/libc++abi layer** -- new/delete, vtables, RTTI, diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index fae3fb46e..7ba0cab02 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -853,3 +853,130 @@ THE HONEST ORDERING *Nothing here is started and none of it is a prerequisite for anything already working.* The next EDG step is still the one in the section above: cpfe translating a toy and `pcc -c' accepting the C. + + +cpfe's TARGET IS NOT kencc's, AND kencc_targ.h NEVER REACHED target.c +---------------------------------------------------------------------- + +`cpfe --c++14 ... t.cpp' answers + + Internal error: check_target_config: must use SoftFloat library + for this configuration + +and that is cpfe refusing its OWN configuration, not anything about the +input. `src/target.c:10642' fires when `targ_ldbl_mant_dig == 64', an +80-bit x87 long double, which cpfe cannot constant-fold without a +SoftFloat library it was not built with. + +cpfe IS MULTI-TARGET. `target.c:6525' holds a table of EIGHT +configurations -- linux_x86_64, linux_i686, win64, win32, +linux_aarch64, linux_armv7, linux_riscv64, linux_riscv32 -- +`fe_init.c:49180' defaults the index to 0, and `--target ' +(cmd_line.c) selects by name. **Entry 0, labelled `linux_x86_64', +points at `set_legacy_target_config'** -- the configuration built from +the UNSUFFIXED TARG_* macros, which is exactly the set `kencc_targ.h' +overrides. + +AND IT CARRIES THE HOST'S VALUES, measured from the generated C: + + set_legacy_target_config: long 8 long double 16 mant_dig 64 + set_target_config_win64: long 4 long double 8 mant_dig 53 + +So `kencc_targ.h' did not reach the translation of `target.c'. **That +is a DIFFERENT macro environment from the one the NOTE above measured.** +The `__weak__' suppression (38,245 -> 0) is a property of the cpfe +BUILD -- how the host cpfe behaved; the TARG_* widths are read when +`target.c' is TRANSLATED, and only the second bakes into the committed +C. The two were treated as one. +*Which mechanism is NOT settled*: the pre-include may have been absent +from the translation command, or present and then overridden by +`cmake_defines.h' included after it. One look at the host build's +translation line separates them, and nobody should act on a guess here. + +win64 IS kencc's INTEGER AND FLOAT MODEL EXACTLY, AND THE DIFF IS THE +WHOLE COST +--------------------------------------------------------------------- +Both configurations are in the generated source, so their diff is a +COMPLETE list rather than a reading -- the `apeabi-probe' idiom again. +126 `targ_*' assignments each, **36 differ**, and they partition: + +RIGHT for kencc, and legacy had them wrong: + targ_sizeof_long 4, alignof_long 4 kencc long IS 32-bit + targ_sizeof_long_double 8, ldbl_mant_dig 53, + ldbl_max_exp 1024, ldbl_min_exp -1021 long double IS double + targ_size_t_int_kind unsigned long long APE stddef_arch.h + targ_ssize_t_int_kind long long APE stddef_arch.h + targ_ptrdiff_t_int_kind long long APE stddef_arch.h + targ_setjmp_func "setjmp" <-- see below + +**`targ_setjmp_func' is the corroboration.** The NOTE above records +`_setjmp' as the one undefined symbol at the link and calls it "a +missed line in kencc_targ.h"; legacy says `_setjmp' and win64 says +`setjmp'. *That the diff independently reproduces a bug found at the +LINK, from a completely different direction, is what says the diff is +measuring the right thing.* + +WRONG for kencc -- the "windows baggage", and it is FOUR families and +one stray, not a vague risk: + 1. wchar_t / wint_t = unsigned short. APE's `stddef.h:50' says + unsigned INT. Two bytes against four; definite mismatch, and it + corrupts every L"..." literal and wchar_t layout. + 2. FIELD ALIGNMENT, five variables: double_field_alignment 4 (legacy + 8), long_field_alignment 4, long_double_field_alignment 4, + dual_alignments_for_builtin_types 1, maximum_intrinsic_alignment + 8 (legacy 16). **This is MSVC struct layout and it is the thing + most likely to bite**, because kencc under `-J' aligns a double + to 8. Not yet measured; `structalign-test' is the instrument + shape for it once anything compiles. + 3. BIT FIELDS: force_one_bit_bit_field_to_be_unsigned 1, + zero_width_bit_field_affects_struct_alignment 1, + zero_width_bit_field_alignment 4. + 4. POINTER-TO-MEMBER: sizeof_ptr_to_data_member 4 (legacy 8), + alignof 4, delta_int_kind `short' (legacy `long'). + 5. The one NON-`targ_' global the win64 config sets: + `packing_applies_to_base_classes = 1'. + +AND THE TWO MICROSOFT SWITCHES ARE OFF, checked because the name +invites the worry: `targ_microsoft_bit_field_allocation' and +`targ_microsoft_ptr_to_member_sizing' are both explicitly 0 in win64, +and the configuration sets NO mode flags at all -- 126 `targ_*' +assignments plus that one. *`--microsoft' is a separate command-line +option and `--target win64' does not imply it.* + +Watch `jmp_buf' too, recorded rather than resolved: win64 says 39 +elements of `int', legacy 25 of `long', and APE's own is neither. +It matters here more than it would elsewhere because **EDG's exception +model is setjmp-based** (see the standard-library section above). + +THE PREDEFINED MACRO FILE IS WHERE THE REST OF THE WINDOWS-NESS LIVES +--------------------------------------------------------------------- +With `--target win64' the SoftFloat check passes and the next stop is + + catastrophic error: cannot open predefined macro file + "lib_win64/predefined_macros.txt" + +`macro.c:19726''s `open_predefined_macro_file' builds +`edg_base_directory' + `auxiliary_info_dir_name' + the file name, so +the directory is per-target and we ship none of them. +**`--clear_flag=use_predefined_macro_file' is the answer, and it is not +a workaround**: that file is where a win64 build's `_WIN32' and +`_MSC_VER' would come from, so suppressing it is precisely "LLP64 +without the Windows macros". It is also the flag the NOTE's own +translation recipe already uses. + + cpfe --target win64 --clear_flag=use_predefined_macro_file \ + --c++14 --no_exceptions --no_rtti \ + --sys_include=/include_c++ \ + --sys_include=/include_c99 \ + --sys_include=/amd64/include/ape \ + --sys_include=/sys/include/ape \ + --gen_c_file_name=/tmp/t.c /tmp/t.cpp + +THE REAL FIX IS THE LEGACY ENTRY, NOT win64. `--target win64' is a +BRIDGE to a first translation with four named divergences; the correct +end state is index 0 carrying kencc's own values, which is what +`kencc_targ.h' was written for and demonstrably did not do. That is a +re-translation with the pre-include actually in effect for `target.c', +and it would fix `wchar_t', the field alignments, pointer-to-member, +`jmp_buf' and `setjmp' in one go -- *every item on the list above is a +symptom of the same miss*. From 633e8da7ea5b29dd0ef87ad756e3943c3cbb2531 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:58:36 +0000 Subject: [PATCH 4/4] edg: cpfe faults past the target flags; host does not, static init is not it With --clear_flag=use_predefined_macro_file cpfe accepts the target and the option and then faults at addr=0x68. Option parsing and target selection are both past -- one flag earlier the same binary gave a clean catastrophic error -- so the fault is in real work. The host cpfe, built from the same commit with the same target configuration and given the same flags and the same four --sys_include directories, exits 0 and writes the expected C. That puts the fault on the kencc/libap side rather than on EDG's. The static-init gap was the obvious suspect and is refuted by measurement: the 133-file corpus holds exactly one __sti__ routine, in fe_init.c, and its body assigns 0 to sixteen members of diagnostic_counters -- values a BSS global already has. The gap remains real for programs cpfe translates and is inert for cpfe itself. Next steps recorded in the NOTE: a sys: trap leaves the process Broken, so acid plus lstk(), whether any C was written, and whether an empty input faults at the same pc. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 17 ++++++++++++ sys/src/external/edg/NOTE | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 706114102..ac6871d57 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2198,6 +2198,23 @@ live either way. state is entry 0 carrying kencc's values, and *every item on the divergence list is a symptom of the same miss*. Command and full diff in the NOTE. + - **AND WITH THAT FLAG cpfe FAULTS -- `fault read addr=0x68 + pc=0x7e2d88` -- WHICH IS NOT A CONFIGURATION PROBLEM.** Option + parsing and target selection are both past (the same cpfe gave a + clean `catastrophic error` one flag earlier), so it dies in real + work. **The HOST cpfe, same commit, same flags, same four + `--sys_include` directories, exits 0 and writes the expected C** -- + so this is the kencc/libap side, the cfront partition again. + **The static-init gap is NOT it, and that is measured rather than + assumed**: the whole 133-file corpus holds exactly ONE + `__sti__` routine (`fe_init.c:55031`) and its body assigns **0** to + sixteen members of `diagnostic_counters` -- values a BSS global + already has. *The gap is real for what cpfe TRANSLATES and inert + for cpfe itself; two different claims.* A `sys:` trap leaves the + process **Broken and readable**, so the next step is `acid ` + and **`lstk()`** (locals, not `stk()`'s arguments -- twice burnt), + then whether `/tmp/t.c` exists and whether an EMPTY input faults at + the same `pc`. - **THERE IS NO C++ STANDARD LIBRARY HERE AND EDG SHIPS NONE, and the EXCEPTION MODEL decides what could fill the gap.** `lib_src` is the **libsupc++/libc++abi layer** -- new/delete, vtables, RTTI, diff --git a/sys/src/external/edg/NOTE b/sys/src/external/edg/NOTE index 7ba0cab02..6ae12be3f 100644 --- a/sys/src/external/edg/NOTE +++ b/sys/src/external/edg/NOTE @@ -980,3 +980,58 @@ re-translation with the pre-include actually in effect for `target.c', and it would fix `wchar_t', the field alignments, pointer-to-member, `jmp_buf' and `setjmp' in one go -- *every item on the list above is a symptom of the same miss*. + + +AND THAT COMMAND FAULTS ON THE VM -- `fault read addr=0x68 +pc=0x7e2d88' -- WHICH IS NOT A CONFIGURATION PROBLEM AT ALL +---------------------------------------------------------------- + +With the flag in place cpfe no longer refuses: it accepts the target, +accepts the option, and then dies inside real work. *Which is itself +the first piece of information*: with `--target win64' alone the same +cpfe gave a clean `catastrophic error' about the macro file, so option +parsing and target selection are both past, and the fault is +downstream of them. + +**THE HOST DOES NOT CRASH ON THE SAME COMMAND, which puts this on the +kencc/libap side rather than on EDG's.** The host `cpfe' built from +the same commit with the same target configuration, given the same +flags and the same four `--sys_include' directories (APE's headers +staged), exits **0** and writes the expected C: + + /* Translated by the Edison Design Group C++/C front end */ + extern int __EDGCPFE__7_0; + struct S; struct S { int x;}; + extern int _Z1fP1S(struct S *s); ... + +So the two runs differ in the compiler that built cpfe and in nothing +else that has been identified. *That is the same partition cfront's +crash had*, and there the answer was a layout rule. + +THE STATIC-INIT GAP IS NOT THIS, AND IT IS NOW MEASURED RATHER THAN +ASSUMED. It was the obvious suspect: `-D_main=__edg_main' means +nothing calls `_Z12__call_ctorsv', so a C++ global needing dynamic +initialisation would stay zero and be dereferenced -- exactly the +shape of a read at a small offset from null. **One grep settles it: +the whole 133-file corpus contains exactly ONE static-init routine**, +`__sti___9_fe_init_c_2cbd8fba' in `src/fe_init.c:55031', and its whole +body assigns **0** to the sixteen members of +`edg::diagnostic_counters' -- values a BSS global already has. So not +calling it costs cpfe nothing. *The gap is real for programs cpfe +TRANSLATES and inert for cpfe itself*, and those are two different +claims; this one is now the measured half. + +WHAT TO ASK NEXT, in order, because a `sys:' trap leaves the process +BROKEN and therefore readable: + + 1. `ps | grep cpfe' -- it should still be there. + 2. `acid ' then **`lstk()'**, not `stk()': this tree has twice + had `stk()''s arguments name the wrong mechanism while the + LOCALS named the right one. Then `echo kill > /proc//ctl', + since a Broken process keeps its memory. + 3. `ls -l /tmp/t.c'. If cpfe wrote any C before dying it reached + the back end and the fault is in wrap-up; an absent or empty file + puts it in the front end. + 4. The same command on an EMPTY `.cpp'. Same `pc' means the input + is irrelevant and the fault is in start-up; a different outcome + means it is in translating this particular source.