Skip to content

Generate the RTS generic apply code in GHC and link it at link time (9.14 port) - #194

Draft
andreabedini wants to merge 10 commits into
stable-ghc-9.14from
wip/andrea/autoapply-link-9.14
Draft

andreabedini wants to merge 10 commits into
stable-ghc-9.14from
wip/andrea/autoapply-link-9.14

Conversation

@andreabedini

@andreabedini andreabedini commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

This is the genapply-in-compiler + autoapply-link series (see wip/andrea/autoapply-link, which is on master) rebased onto stable-ghc-9.14, plus one commit for this tree's Makefile build.

What changes

The generic apply code (stg_ap_*_fast, the stg_ap_* return frames, stg_ap_stk_*, stg_stk_save_* and the ARG_*-indexed tables) is no longer produced by utils/genapply and compiled into libHSrts. GHC generates it itself from the compiler's own tables (GHC.StgToCmm.AutoApply, ghc --gen-apply), and links it into every program at link time, into -shared outputs that contain the RTS (-flink-rts, Windows, wasm) and into -staticlib archives. The RTS only refers to the symbols. -fno-link-autoapply turns this off; ghc --gen-apply[=v16|v32|v64] prints the Cmm for anyone linking by other means. See Note [Linking the generic apply code] in GHC.Driver.Pipeline and the new users guide section.

Consequence worth knowing: a Haskell shared library built without -flink-rts and loaded by a program GHC did not link has no provider for stg_ap_* any more. It must be built with -flink-rts, or the code must be generated and linked by hand; both are documented. Programs and libraries linked by GHC need no changes.

Commits:

  1. compiler: add GHC.StgToCmm.AutoApply and the --gen-apply mode
  2. hadrian: generate AutoApply.cmm with ghc --gen-apply; remove utils/genapply
  3. StgToCmm: derive slowCallPattern and stdPattern from the apply tables
  4. compiler: link the generic apply code into programs at link time
  5. rts: drop AutoApply.cmm from libHSrts; hadrian: stop generating it
  6. docs: document linking the generic apply code and the --gen-apply mode
  7. build: drop genapply from the Makefile build and the AutoApply plumbing from rts.cabal (9.14 only)
  8. rts: generate and link the Jumps.h variants at link time too
  9. compiler: move the link-time RTS Cmm catalogue to GHC.Linker.RtsCmm
  10. compiler: fix the instruction-set level of the link-time RTS Cmm objects (from Copilot's review: the baseline objects inherited -mavx* from the link command line; now pinned per variant)

Commits 2–6 leave the Makefile build referring to genapply; 7 fixes that. The .cmm.h wrapper scheme in rts/configure.ac and rts.cabal goes away with it.

Commit 8 came out of a review comment: stg_stack_underflow_frame_* and stg_restore_cccs_* (rts/Jumps.h) are in the same situation as the apply code. They exist once per argument-register width, the V32/V64 variants need the same per-file -mavx flags, the RTS refers to them the same way, and they encode the argument-register cover that the compiler already has in realArgRegsCover. So GHC now also writes the four two-line wrappers around the installed Jumps.h and compiles them at link time (GHC.StgToCmm.AutoApply.genFiles lists all eight files; --gen-apply accepts jumps, jumps-v16, jumps-v32, jumps-v64). rts.cabal ends up with no per-file cmm options at all, and hadrian loses its Jumps -mavx rules.

Commit 9 moves the list of link-time files (GenFile, genFiles, the Jumps wrapper) out of GHC.StgToCmm.AutoApply, which is left with the apply-code generator only, into a new GHC.Linker.RtsCmm; the pipeline entry points are now mkRtsCmmObjs/rtsCmmObjsForLink. The compile step stays in GHC.Driver.Pipeline because it runs cmmCppPipeline. User-facing names (--gen-apply, -flink-autoapply) are unchanged for now; both now also cover the Jumps frames, so renaming them before this merges is worth a thought.

Differences from the master version

  • rts/include/rts/storage/FunTypes.h lives in rts-headers/ here.
  • DynFlags has avx2 :: Bool rather than sseAvxVersion.
  • No GHC.Platform.Tag: the generator uses GHC.Cmm.Utils.mAX_PTR_TAG and GHC.StgToCmm.Closure.isSmallFamily.
  • link/link' in GHC.Driver.Pipeline took the pieces of an HscEnv rather than the HscEnv; the link-time generator needs the real thing, so they now take an HscEnv as on master. GHC.Driver.Make is the only caller.
  • No changelog.d/ entry (the directory does not exist on this branch).

Verification

make stage2 with the Makefile defaults (ghc-9.8.4 boot) on x86_64 Linux:

  • stage1 and stage2 build; the RTS configures without genapply and builds without AutoApply; every executable in _build/dist/bin links against it.
  • With the stage2 ghc: a test program runs plain and -threaded, with the stg_ap_* symbols and the eight Jumps frames coming from the link-time objects; a C main linked with -no-hs-main works; -fno-link-autoapply fails with undefined stg_ap_*/stg_arg_bitmaps, as it should; the manual recipe from the users guide (eight --gen-apply files, ghc -c, link with -fno-link-autoapply) works. The four libHSrts*.a only reference the Jumps frames now (16–20 U entries each, no definitions).
  • ghc --gen-apply output diffed against the old 9.14 genapply on the same DerivedConstants.h: identical for all four files except the intended preamble, where the 94 lines of BUILD_PAP/NEW_PAP/jump_SAVE_CCCS macros replace #include "AutoApply.h".

make then failed in the dist step at patchelf --set-rpath with "virtual address space underrun" (patchelf 0.18.0) on ghc, ghc-iserv and haddock. The untouched stage1 ghc built by 9.8.4 fails the same way, so that looks like a patchelf problem on my machine rather than this branch; the remaining ghc-pkg recache/check steps pass when run by hand.

Not done: link-time cost. Four Cmm compiles are added to every link (about 0.2s → 0.8s for hello-world with a quick-flavour master compiler; not measured here). Caching the objects per way is the obvious follow-up.

🤖 Generated with Claude Code

andreabedini and others added 7 commits September 21, 2026 14:50
The RTS's generic application code (rts/AutoApply.cmm and the
AutoApply_V{16,32,64}.cmm vector variants) has so far been produced by the
standalone utils/genapply program, which re-implements things the compiler
already owns: register assignment, argument representations, the pointer-tag
arity check, and eight target constants that it scraped from comment lines
which deriveConstants wrote into DerivedConstants.h for its benefit (#24347).

This adds GHC.StgToCmm.AutoApply, a port of the generator that is driven by
the compiler's Platform: registers come from GHC.Cmm.CallConv.assignArgumentsPos
(the same function used for compiled call sites), the constants from
platformConstants, and the argument tables live in GHC.StgToCmm.ArgRep as
applyTypes and stackApplyTypes, following the structure of GHC.StgToJS.Apply.

It is exposed as a new mode, `ghc --gen-apply[=v16|v32|v64]`, which writes the
Cmm source to the -o file or to stdout.  The output is Cmm text, so the RTS's
CPP for PROFILING, REG_YMM1 and friends and the per-file -mavx split are
untouched, and the result is byte-for-byte identical to genapply's output
(checked on x86_64 for all four files; only the header comment differs).

One deliberate special case: an L argument is only ever loaded into a long
register.  On 64-bit targets assignArgumentsPos would use a vanilla register,
but toArgRep never produces L there, so the stg_ap_l routines are dead code
and are kept unchanged.

genapply itself is still in place; the build system is switched over in the
next commit.
…napply

Replace the stageBoot genapply program with a `GenApply` mode of the Ghc
builder.  The AutoApply*.cmm files of a stage's RTS are now generated by the
same compiler that compiles that RTS, with the RTS's DerivedConstants.h
passed via -I so that GHC can read the platform constants while the RTS unit
is not yet in a package database.

Consequently utils/genapply is deleted, and deriveConstants no longer emits
the `// KEY VALUE` comment lines that only genapply consumed.  Comments that
pointed at genapply now point at GHC.StgToCmm.AutoApply.

The generated files are identical to those produced by genapply.
slowCallPattern (which stg_ap_* routine to call for an unknown call) and
stdPattern (which ARG_* descriptor a function gets) were hand-written mirrors
of the pattern tables that genapply used.  Now that those tables live in
GHC.StgToCmm.ArgRep as applyTypes and stackApplyTypes, derive both functions
from them: slowCallPattern picks the longest table pattern that is a prefix of
the argument list, stdPattern looks the pattern up by its index.

Behaviour is unchanged.  In particular stdPattern still does not return
ARG_PPPPPPP or ARG_PPPPPPPP, although the RTS has entries for them; using
them would change compiled code and is left for a separate, measured change.
Instead of being compiled into libHSrts, the RTS's generic application code
(stg_ap_*_fast, the stg_ap_* return frames, stg_ap_stk_*, stg_stk_save_* and
the ARG_*-indexed tables) is generated by GHC.StgToCmm.AutoApply when a
program is linked, compiled through the normal Cmm pipeline into temporary
objects, and added to the link -- the same way the JS backend emits rts.js at
link time.  See Note [Linking the generic apply code] in GHC.Driver.Pipeline.

The objects go wherever the RTS goes: into every executable (also with
-no-hs-main, and the generated iserv), into -shared outputs that contain the
RTS (-flink-rts, Windows, wasm; see the new GHC.Linker.Dynamic.dynLibLinksRts)
and into -staticlib archives unless -fno-link-rts.  Nothing is generated when
the RTS is not among the linked units (e.g. GHC linking a plain C program).

The generated code follows the RTS way being linked: -DPROFILING arrives via
wayOptc; THREADED_RTS, DEBUG and TICKY_TICKY (non-threaded debug only) are
added to match the defines Hadrian builds those RTS ways with.  The V16/V32/
V64 routines stay separate objects compiled with -mavx2/-mavx512f on x86.
Since only installed RTS headers are available at link time, the generator
now emits the BUILD_PAP/NEW_PAP/jump_SAVE_CCCS macros itself instead of
including rts/AutoApply.h.

The new -f[no-]link-autoapply flag (default on) turns this off; `ghc
--gen-apply` still prints the Cmm for anyone linking by other means.

Measured on x86_64 with a quick-flavour (-O0) compiler: linking a hello-world
executable takes ~0.8s instead of ~0.2s, the four Cmm compiles accounting for
the difference; the executable size is unchanged.  A release-flavour compiler
should narrow this, and the objects could be cached per way if needed.
The RTS now only refers to the generic apply symbols; GHC generates and
links the code into each program (previous commit).  Remove the generated
AutoApply*.cmm from rts.cabal, drop the misleading RTS_PRIVATE from the
table declarations in rts/Apply.h (the definitions are provided by the
program's link and were always exported anyway), and delete rts/AutoApply.h,
whose macros are now emitted by the generator.

Hadrian loses the AutoApply generation rules, the `GenApply` GhcMode and the
per-file -mavx2/-mavx512f flags for AutoApply_V*.cmm (the Jumps_V* ones
stay).  `ghc --gen-apply` itself is kept.
The previous commits moved the generic apply code out of libHSrts and into
every link that GHC performs.  Say so where users will look for it:

  * a --gen-apply entry in the modes of operation;
  * a new "Linking the runtime's generic apply code" section in the FFI
    chapter, covering when GHC adds the code, when it does not (a shared
    library built without -flink-rts loaded by a program GHC did not link)
    and how to generate, compile and link it by hand, including the way
    defines and the -mavx flags for the vector files;
  * the shared-libraries chapter no longer says that omitting -flink-rts
    and linking the RTS into the final executable always works;
  * cross-references from -flink-autoapply and the -no-hs-main note.

Also add a changelog.d entry (MR number to be filled in).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng from rts.cabal

The previous commits removed utils/genapply and moved the generic apply code
out of libHSrts; this tree builds with the Makefile rather than hadrian, so
take the tool out of that build too:

  * Makefile, cabal.project.stage1, cabal.project.stage2.common and the nix
    CI workflow no longer build, export or ship genapply;
  * rts/configure.ac no longer looks for genapply or generates the
    AutoApply*.cmm.h headers;
  * rts.cabal drops the generated AutoApply*.cmm.h from autogen-includes and
    install-includes together with the "[The AutoApply story]" note.  The
    per-file -mavx2/-mavx512f options that were only there for the AutoApply
    wrappers went with the wrappers in the previous commit; the ones on
    Jumps_V32/V64.cmm are still needed and stay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 21, 2026 07:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

V16 generation must clear inherited AVX feature flags before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Moves generic apply-code generation from utils/genapply and libHSrts into GHC, generating and linking it at final link time.

Changes:

  • Adds compiler-integrated --gen-apply generation and automatic linking.
  • Removes obsolete RTS, Makefile, and Hadrian generation plumbing.
  • Documents automatic and manual generic-apply linking.
File Reviewed changes
utils/​genapply/​hie.yaml Removes obsolete utility configuration.
utils/​genapply/​genapply.cabal Removes the standalone generator package.
utils/​deriveConstants/​Main.hs Removes generator-specific metadata.
rts/​rts.cabal Removes AutoApply sources and headers from RTS builds.
rts/​include/​stg/​MiscClosures.h Updates generic apply declarations.
rts/​configure.ac Removes genapply configuration.
rts/​AutoApply.h Removes obsolete helper macros.
rts/​AutoApply.cmm Removes the obsolete wrapper.
rts/​AutoApply_V64.cmm Removes the obsolete wrapper.
rts/​AutoApply_V32.cmm Removes the obsolete wrapper.
rts/​AutoApply_V16.cmm Removes the obsolete wrapper.
rts/​Apply.h Updates generated symbol declarations.
rts-headers/​include/​rts/​storage/​FunTypes.h Documents generic apply table ordering.
Makefile Removes genapply build plumbing.
hadrian/​src/​Settings/​Packages.hs Removes obsolete RTS apply flags.
hadrian/​src/​Settings/​Default.hs Removes the generator package and builder.
hadrian/​src/​Settings/​Builders/​GenApply.hs Deletes obsolete builder configuration.
hadrian/​src/​Rules/​Generate.hs Removes AutoApply generation rules.
hadrian/​src/​Rules/​Compile.hs Removes generated-Cmm special handling.
hadrian/​src/​Packages.hs Removes the genapply package definition.
hadrian/​src/​Context.hs Updates object-path documentation.
hadrian/​src/​Builder.hs Removes the GenApply builder.
hadrian/​hadrian.cabal Removes the deleted builder module.
ghc/​Main.hs Implements --gen-apply.
ghc/​GHC/​Driver/​Session/​Mode.hs Adds genapply mode parsing.
docs/​users_guide/​using.rst Documents --gen-apply.
docs/​users_guide/​shared_libs.rst Documents shared-library linking requirements.
docs/​users_guide/​phases.rst Documents -flink-autoapply.
docs/​users_guide/​exts/​ffi.rst Documents manual generic-apply linking.
docs/​stg-spec/​stg-spec.mng Updates generator references.
compiler/​GHC/​StgToCmm/​Layout.hs Derives standard patterns from shared tables.
compiler/​GHC/​StgToCmm/​AutoApply.hs Adds compiler-side Cmm generation.
compiler/​GHC/​StgToCmm/​ArgRep.hs Adds shared apply tables and naming helpers; stale implementation references should be updated.
compiler/​GHC/​Runtime/​Interpreter/​Init.hs Supports extra interpreter link objects.
compiler/​GHC/​Runtime/​Interpreter/​C.hs Links extra objects into generated iservs.
compiler/​GHC/​Linker/​Dynamic.hs Centralizes RTS-linking detection.
compiler/​GHC/​Driver/​Session.hs Registers the link-autoapply flag.
compiler/​GHC/​Driver/​Pipeline.hs Generates and links apply objects; V16 must clear AVX flags, with coverage and caching follow-ups noted.
compiler/​GHC/​Driver/​Make.hs Adapts linking to the updated environment API.
compiler/​GHC/​Driver/​Flags.hs Adds the link-autoapply option.
compiler/​GHC/​Driver/​DynFlags.hs Enables auto-apply linking by default.
compiler/​GHC/​Driver/​Config/​Interpreter.hs Initializes extra interpreter link objects.
compiler/​GHC/​Cmm/​CallConv.hs Updates AutoApply references.
compiler/​GHC.hs Supplies generated objects to GHCi.
compiler/​ghc.cabal.in Includes the new compiler module.
cabal.project.stage2.common Removes the deleted utility package.
cabal.project.stage1 Removes the deleted utility package.
.github/​workflows/​nix-ci.yml Removes obsolete genapply packaging.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread compiler/GHC/Driver/Pipeline.hs Outdated
Comment on lines +770 to +773
in case mb_vec of
Just 32 | x86 -> d2 { avx2 = True }
Just 64 | x86 -> d2 { avx512f = True }
_ -> d2

@andreabedini andreabedini Sep 21, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and it was slightly wider than the V16 case: every x86 ISA knob (sseVersion, bmiVersion, avx*, fma) was inherited from the link command line by all the baseline objects, including the scalar/V16 Jumps wrappers.

Fixed in c80685e: the ISA level is now pinned per variant (baseline, avx2, avx512f) and no longer follows the link flags; ways, optimisation level and backend still do. Checked with ghc -mavx2 -keep-tmp-files and objdump -D -j .text on the kept objects: before, stg_ap_v16_fast had 7 VEX-encoded instructions and stg_stack_underflow_frame_v16 12; after, both have none, while the V32/V64 objects keep their ymm/zmm code (7 and 12 respectively) and the scalar ones are unchanged.

🤖 Generated with Claude Code

andreabedini and others added 3 commits September 21, 2026 16:31
stg_stack_underflow_frame_* and stg_restore_cccs_* (rts/Jumps.h) exist
once per argument-register width for the same reason the generic apply code
does, need the same per-file -mavx2/-mavx512f flags on the V32/V64
variants, and are referred to by the RTS in the same way.  Treat them the
same way: GHC now writes the four two-line wrappers around the installed
Jumps.h and compiles them along with AutoApply*.cmm when it links the RTS
into something (GHC.StgToCmm.AutoApply.genFiles lists all eight files, see
the new Note [Link-time RTS Cmm files]), and `ghc --gen-apply` accepts
`jumps`, `jumps-v16`, `jumps-v32` and `jumps-v64` so the manual recipe in
the users guide still covers everything.

rts.cabal loses Jumps_*.cmm and with them the last per-file cmm options;
hadrian loses the corresponding -mavx rules.  Jumps.h itself stays in the
RTS as an installed header.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GHC.StgToCmm.AutoApply had grown a second job: besides generating the
generic apply code it listed every Cmm file GHC compiles when it links the
RTS, including the rts/Jumps.h wrappers, which have nothing to do with
StgToCmm.  Move that catalogue (GenFile, genFiles, genFile, genFileVecWidth,
genJumps and Note [Link-time RTS Cmm files]) to a new GHC.Linker.RtsCmm,
next to GHC.Linker.Dynamic's dynLibLinksRts, and leave AutoApply.hs with
genAutoApply only.  The pipeline entry points follow the new name:
mkAutoApplyObjs and autoApplyObjsForLink become mkRtsCmmObjs and
rtsCmmObjsForLink.  The compile step itself stays in GHC.Driver.Pipeline
because it runs cmmCppPipeline.

No change to the user-facing --gen-apply mode or -flink-autoapply flag.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mkRtsCmmObjs compiled the generated Cmm with the DynFlags of the link
command line and only added -mavx2/-mavx512f for the V32/V64 variants.  So
`ghc -mavx2 ... -o prog` (or a cabal ghc-options that reaches the link
step) compiled AutoApply.cmm, AutoApply_V16.cmm and the scalar/V16 Jumps
wrappers with AVX enabled, although those objects go into every program
and used to be built at the RTS's baseline.  Pin the x86 ISA knobs per
variant instead: reset sseVersion, bmiVersion, avx*, fma to the baseline,
then enable exactly avx2 for V32 and avx512f for V64.  Found by review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@andreabedini
andreabedini marked this pull request as draft October 9, 2026 08:42

This branch has not been deployed

No deployments
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