Skip to content

M9: Third target, third host, Postgres #10

Open

Description

@tamnd

The third target and the third host, and PostgreSQL.

RISC-V 64 is what spec/10-backend.md calls the middle-end canary: an architecture different enough from x86-64 and AArch64 that a middle-end assumption baked into both of them shows up as a failure rather than as slightly worse code. Windows brings COFF, a fourth ABI and SEH unwind tables, and it is the host most likely to find something the other two tolerated.

Exit criterion. The full six-row CI matrix is green, make check-world passes, pgbench is within the bound, and a GCC-built extension loads into a rucc-built server.

RISC-V 64

  • The rule set, discharged by rucc-verify
  • The encoder, and relaxation
  • The calling convention
  • Whatever the middle end assumed and should not have

Windows

PostgreSQL

  • Builds unpatched at all levels
  • make check-world passes
  • pgbench within the code quality bound, published including any losses
  • A GCC-built extension loads into a rucc-built server, which is the real ABI test

CI

What is explicitly not in scope

The kernel, and the hardening flags it needs. Those are M10 and M11.

This is the second sane stopping point, and the strongest one. At the end of M9 the project has met three of its four axes on three hosts and three targets against two large real databases, with continuous fuzzing and verified rewrite rules. Whether to attempt the kernel from here is a decision made with far better information than we have now.

Activity

  1. added
    kind/milestoneA milestone from spec/17-milestones.md, tracked as one issue
    status/gateAn exit criterion. The milestone is not done until this is.
    area/objectsELF, Mach-O and COFF writers
    area/abiThe four ABIs, struct layout, TLS, and the runtime
    area/riscvThe RISC-V 64 target
    area/windowsWindows as a host and as a target
    on Aug 31, 2026
  2. tamnd commented on Sep 18, 2026

    @tamnd
    OwnerAuthor

    #1435 lands the first half of the SEH unwind tables line above. A function that keeps a frame pointer on x86_64-windows-gnu now reaches an object file with a record in it, which it never did before, and that covers every variable length array, every alloca and every setjmp on the target. The record a function carries on Windows counts every slot from where the stack pointer ends the prologue, so the order the prologue had, which is push the pointer, point it at the frame, then take the frame, has no rows the format can write. The fix reorders the prologue on that target only: push everything, take the frame, and only then point the register at it, with the move emitting a DefCfa that the writer turns into UWOP_SET_FPREG. That is the shape gcc 13.2 emits for x86_64-w64-mingw32, measured on server3 rather than read off a manual, and what it costs is the chain of frame pointers, which nothing on Windows walks.

    Verified end to end: a program with all three shapes of frame in it compiles here, links with the mingw driver, runs under wine and prints the right answers, and its three .xdata records decode by hand to the prologues the disassembly shows.

    The line stays unticked because one shape is still refused rather than described, which is a frame that forces its own alignment past sixteen bytes. The and mask leaves the pushes at no constant distance from anything, so the pointer has to go up before the mask, and describing that needs a second base register. It is filed as #1422.

  3. tamnd commented on Sep 18, 2026

    @tamnd
    OwnerAuthor

    #1442 has landed, which defines __SEH__ for x86_64-windows-gnu and closes the last gap in the predefined macro set for this target. It was held back by #1403 rather than by anything about the macro: mingw's setjmp.h reads it to choose the two argument _setjmp, whose second argument is __builtin_frame_address(0), and a function that asks for that used to be refused by the unwind writer.

    The pleasant part is that the two halves fit without anything having to be arranged. longjmp hands that second argument to RtlUnwindEx as the frame to unwind to, which must be the establisher frame the target function's own unwind record reports, and the late prologue leaves the frame pointer holding exactly that address. So the number the builtin copies out of the register and the number the walk arrives at are the same number by construction.

    Measured on server3. A program that longjmps out of four frames of recursion back past a setjmp, with a variable length array after it, prints the same answers under wine whether gcc 13.2 or this compiler built it, and the object passes %rbp to _setjmp where gcc passes %rbp. setjmp.h is the only header in the mingw tree that reads the macro, so nothing else changes shape.

  4. tamnd commented on Sep 18, 2026

    @tamnd
    OwnerAuthor

    Rung 1 is done, and the thing that stood in its way was one line of the code generator rather than anything about the tree.

    sqlite3.c compiled against the fetched sysroot on the first try, in 21 seconds, with nothing said on the command line about where the headers are. shell.c did not: it stopped at nothing reaches 'sqlite3_free' through a table here [E0652], because the shell stores that function in a table of callbacks and every function a file only declares went into the set of names the instruction pointer cannot reach. That set exists for the global offset table and COFF has no such table, so the object writer was handed a relocation it has no spelling for and refused the file. On this format the question does not arise, since a name the link resolves out of another object is in the image and a name from a DLL arrives through an import library holding a jump under the plain name, which is why gcc writes leaq other(%rip), %rax there. That is #1443 and #1448 fixed it.

    With that in, both objects compile, link with x86_64-w64-mingw32-gcc and run under wine. -M says every header the build read came out of /root/.cache/rucc/sysroots/x86_64-windows-gnu, which is the tree rucc --fetch x86_64-windows-gnu installs and nothing else. The shell prints 3.53.4 2026-07-24 19:02:57 bf7c7f30... for -version and answers 3|6|one,two,three to a create, an insert of three rows and a select.

    One rung left here, which is the row in section 13.1 with the installed size. The tree measures 85 MiB installed with an empty lib directory, so the import library question the description raises is still open and is what that row has to be honest about.

  5. tamnd commented on Sep 18, 2026

    @tamnd
    OwnerAuthor

    All five rungs are done, so this closes.

    The last one was the row in section 13.1, and the interesting part of it was that the estimate was wrong by a factor of five. The tree was written down as 15 MB and it is 80.5 MB, which is 1,702 headers and 8.5 MB gzipped, and that is larger on its own than the 75 MB the whole base distribution is budgeted for. So the row moved out of the base and into the cache of section 13.2, which is where the compiler was already putting it, and the rows above the line come to 55.9 MB.

    Where the bytes go makes the number stop being surprising. mshtml.h alone is 6.5 MB, more than the entire glibc header tree, and ddk is 2.2 MB and GL is 1.4 MB before any of it has declared a C library function. That is this issue's own observation about the Windows SDK half of the tree, arriving as a budget line.

    One question is left open and is named as open in the document rather than left implied: whether the published tree should be all of upstream or a subset. It is a question about which headers a program on this target may include rather than a question about bytes, so it waits for a program a subset would break.

    The whole issue in order: #1403 for the unwind record, #1442 for __SEH__, #1443 for the address of a declared function on COFF, and #1449 for the row. What is still not here is the import libraries, so the published tree has an empty lib and a link still names mingw's own, which is the next thing in front of #618 for this target.

  6. tamnd commented on Sep 28, 2026

    @tamnd
    OwnerAuthor

    The Postgres section of this milestone is now split into ten milestones, PG0 to PG9, each with its own issue and exit criterion. They are labelled area/postgres. The harness lives in a new repository, tamnd/rucc-postgres, and every compiler change still lands here.

    Issue Rows Estimate
    PG0 #1987 the instrument, the baselines, meson recognizing rucc baselines on all four 4 weeks
    PG1 #1988 builds on Linux x86-64, configure matches GCC L64 5 weeks
    PG2 #1989 main suite, isolation, ecpg, contrib, modules L64 5 weeks
    PG3 #1990 check-world, TAP, autoconf and meson L64 4 weeks
    PG4 #1991 Linux AArch64 LA64 4 weeks
    PG5 #1992 macOS arm64 and the Mach-O writer M64 10 weeks
    PG6 #1993 Windows x86-64 with MinGW W64 8 weeks
    PG7 #1994 the performance gate L64 12 weeks
    PG8 #1995 full configuration, more levels, Postgres 19 all four 5 weeks
    PG9 #1996 thirty green nights all four 3 weeks

    That is about 60 engineer-weeks for one person and about 35 for two, since PG5 and PG6 are independent of each other after PG3, and PG7 can start once PG2 gives a server to measure. The Postgres checkboxes above close when PG3 (check-world), PG7 (pgbench) and PG3's cross module test do, and all of it holds for good when PG9 does. RISC-V and the rest of Windows here are not affected.

  7. tamnd commented on Oct 4, 2026

    @tamnd
    OwnerAuthor

    The Windows part of this is done. W0 through W6 are closed, the last of them released in v0.19.0. x86_64-windows-gnu is tier 2, aarch64-windows-gnu is tier 2, x86_64-windows-msvc links through lld-link, and i686-windows-gnu is tier 3 with rung 0 passing natively through WoW64 and under Wine. The four Windows items above are ticked with the issues that did each. RISC-V, PostgreSQL and the full CI matrix are still open here.

  8. tamnd commented on Oct 17, 2026

    @tamnd
    OwnerAuthor

    #3712 is merged, and "test on windows x86-64" passes on its PR run. The job had failed on main for days with 8 tests in three places, none of them a compiler bug: two tests compared paths as strings and missed a path joined with a \ on Windows, and six lane checks in wasm_simd_lanes linked a host program on a Windows host that has no sysroot in the test job. The two now compare paths, and the host half of the six is left out on Windows when the test is compiled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/abiThe four ABIs, struct layout, TLS, and the runtimearea/objectsELF, Mach-O and COFF writersarea/riscvThe RISC-V 64 targetarea/windowsWindows as a host and as a targetkind/milestoneA milestone from spec/17-milestones.md, tracked as one issuepriority/p1Blocks a milestonestatus/gateAn exit criterion. The milestone is not done until this is.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions