Have you considered providing fil-c as a clang plugin rather than relying on modifying the LLVM source? If that would be possible I see the following benefits:
- It is no longer necessary to build the whole LLVM toolchain, just to get the compiler. Instead only the plugin must be built, which can then be used with existing LLVM/clang installations.
- Way less maintenance work when incorporating changes from upstream in order to update to latest LLVM.
Clang already supports front-end plugins, loaded via -fplugin=xyz.so, and pass plugins, loaded via -fpass-plugin=abc.so.
There are a few downsides to that approach though:
-
Options must be passed using
-plugin-arg-filc <arg>=<value>instead of being first-class citizens of theclangCLI. -
According to Anthropic's Sonnet 5, some of the other patches could be refactored.
Full Sonnet 5 output
What's really patched. Three things, confirmed in code:
- clang::TargetInfo gets two new string fields (DataLayoutStringBeforeFilC/AfterFilC, clang/include/clang/Basic/TargetInfo.h:245-246).
- llvm::Module gets a genuine new field (DLAfterFilC, llvm/include/llvm/IR/Module.h:196) plus getter/setter methods.
- LLVM's .ll text format gets a new keyword, datalayout_after_filc (llvm/lib/AsmParser/LLLexer.cpp:588, LLParser.cpp:658-663), so it round-trips through textual/bitcode IR.
None of that is addable by any plugin — you can't retroactively add a member field to a C++ class or a keyword to a parser that's already been compiled into the toolchain. So the straightforward answer to "does a frontend plugin have access to it" is no: a frontend plugin (-fplugin=, PluginASTAction) gets a CompilerInstance& and can call getTarget(), but on a stock, unpatched Clang binary TargetInfo simply doesn't have these fields — there's nothing to access. And frontend plugins don't register optimization passes anyway; that's -fpass-plugin='s job, a separate mechanism (they can both be loaded in one invocation, but they don't share an API).
But it's less necessary than it looked. I diffed the actual before/after strings for x86_64 Linux (clang/lib/Basic/Targets/X86.h:743-746), and the only difference is the ni:0 token — LLVM's "non-integral pointer" marker for address space 0. Nothing about size, alignment, or mangling changes. ni:0 exists purely to stop LLVM's optimizer from folding ptrtoint/inttoptr round-trips and similar pointer/int identities — which matters before FilPizlonator runs (during KillUB/cleanup/inlining, so the original pointer semantics survive intact for Pizlonator to analyze), and is dropped again once Pizlonator has replaced implicit pointer semantics with its own explicit capability calls, so the standard optimizer can run normally afterward.
That means a pass plugin can reproduce this with zero core-class patches: at the top of its own registerPipelineStartEPCallback (the same extension point Fil-C uses), call the perfectly ordinary, public Module::setDataLayout(StringRef) to append -ni:0 to whatever layout Clang already emitted; run your KillUB/cleanup/inline/Pizlonator-equivalent sequence; call setDataLayout again to strip it before returning control to the standard -O2/-O3 pipeline. No new fields, no new IR syntax — just local state inside your callback closure
Where this breaks down. Fil-C's real fork persists the "after" layout as a first-class Module field with IR-text support because it's read across an actual serialization boundary — clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp uses getDataLayoutStringAfterFilC() when writing/reading precompiled headers. A plugin's in-memory local-variable trick can't survive that, since there's no slot in a .pch/bitcode file to carry it without a real patch. The same problem would resurface under LTO, which also serializes IR between the frontend and backend. If you don't need PCH or LTO, the plugin approach above should just work; if you do, you'd need a workaround like stashing the after-layout as ordinary named module metadata (a legitimate, patch-free extension point) and having your plugin read/act on that metadata — workable, but not as clean as what the fork does natively.
Related
Activity
2I tried to plugin-ify the llvm integration a bit and I guess there's good news and bad news. Good news is much of fil-c can fit into a more modular architecture, but (the bad news) it can't be expressed "as a pure plugin" because there's not enough internal llvm extension points (from what I can tell).
My attempts are here along with some other extension experiments (not all successful: attempted to add darwin/arm64 support, attempted pseudo-lto/bitcode support, and a full cleanup of the shell script graveyard for easier unified running/management):
- https://github.com/mattsta/fil-c/blob/added/plugin-architecture/filc-plugin/docs/compiler-architecture.md#filcpipelinecpp--construction-owner
- https://github.com/mattsta/fil-c/blob/added/plugin-architecture/filc-plugin/docs/operator-architecture.md#command-map-upstream-deluge--this-tree
- https://github.com/mattsta/fil-c/tree/added/plugin-architecture/filc-plugin#readme
- https://github.com/mattsta/fil-c/blob/added/plugin-architecture/patches/llvm-20.1.8/README.md
- https://github.com/mattsta/fil-c/blob/added/plugin-architecture/filc-plugin/docs/from-upstream.md
I'd consider my branch more on the side of "pre-explored ideas for future reference" rather than directly usable as-is.
good luck, season to taste, ymmv, etc.
I'm happy to see contributions that do this, but I do not have time to do it.