Skip to content

[dictgen] Register rootcling options in their own llvm::cl subcommand - #23546

Open
guitargeek wants to merge 1 commit into
root-project:masterfrom
guitargeek:rootcling-options
Open

guitargeek wants to merge 1 commit into
root-project:masterfrom
guitargeek:rootcling-options

Conversation

@guitargeek

Copy link
Copy Markdown
Contributor

rootcling's options were registered in LLVM's top-level subcommand, next to all of LLVM's own options. When linking against a shared libLLVM, both end up in the same registry and names that exist in both abort at startup, for example rootcling's -W and the -W alias for --no-warn from MCTargetOptionsCommandFlags:

: CommandLine Error: Option 'W' registered more than once!

llvm::cl only checks for duplicates within a subcommand. Registering the options in a dedicated rootcling subcommand, which RootClingMain selects by inserting its name as the first argument unless bare-cling was requested, avoids the clash while keeping the command line syntax unchanged. LLVM's options stay in the top-level subcommand, so cling's -mllvm handling is unaffected.

See #17698.

🤖 Done with the help of AI

Less intrusive alternative to #20903.

rootcling's options were registered in LLVM's top-level subcommand, next
to all of LLVM's own options. When linking against a shared libLLVM,
both end up in the same registry and names that exist in both abort at
startup, for example rootcling's `-W` and the `-W` alias for `--no-warn`
from MCTargetOptionsCommandFlags:

    : CommandLine Error: Option 'W' registered more than once!

llvm::cl only checks for duplicates within a subcommand. Registering the
options in a dedicated `rootcling` subcommand, which RootClingMain
selects by inserting its name as the first argument unless `bare-cling`
was requested, avoids the clash while keeping the command line syntax
unchanged. LLVM's options stay in the top-level subcommand, so cling's
`-mllvm` handling is unaffected.

See root-project#17698.

🤖 Done with the help of AI
ferdymercury

This comment was marked as outdated.

@vgvassilev

Copy link
Copy Markdown
Member

Yes, and that signals that we have two copies of llvm which is something we do not want.

@guitargeek

Copy link
Copy Markdown
Contributor Author

That's something you don't want 🙂

@guitargeek

Copy link
Copy Markdown
Contributor Author

Thanks! I tried your patch locally on Ubu26.04 with builtin_llvm=OFF and it looks great.

This will help a lot for Debian packaging.

I would try to keep out the discussion about builtin_llvm=OFF or Debian packaging out of this PR, as this goes way beyond this scope this PR, which is a local improvement to the rootcling options parsing.

@ferdymercury

This comment was marked as outdated.

@guitargeek

guitargeek commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

One question, where are these two copies stored?

Since you're asking: one concrete example would be using ROOT together with a library that bundles its own shared LLVM libraries, like PyTorch. Using a ROOT build linked against shared system LLVM in Python together with PyTorch would therefore crash badly.

If I understand correctly, @vgvassilev wants to make these situations outright impossible by discouraging linking against shared LLVM, which is honorable. But on the other side you have @ferdymercury or me who are interested in Nix or Debian packaging for example, and we don't like handholding about how we should manage dependencies and like to keep the freedom to choose linking against the shared libraries. And in closed ecosystems like Nix or Debian, you can also strive towards the long-term goal of having ROOT and PyTorch both using the same system LLVM, which is not unrealistic in a few years from now.

@github-actions

Copy link
Copy Markdown

Test Results

    23 files      23 suites   3d 19h 4m 10s ⏱️
 3 869 tests  3 865 ✅ 0 💤 4 ❌
79 931 runs  79 927 ✅ 0 💤 4 ❌

For more details on these failures, see this check.

Results for commit a9e8aa4.

@vgvassilev

Copy link
Copy Markdown
Member

That's something you don't want 🙂

It sounds like a preference and it is not: I've put several technical arguments consistently and over time about this.

But on the other side you have @ferdymercury or me who are interested in Nix or Debian packaging for example, and we don't like handholding about how we should manage dependencies and like to keep the freedom to choose linking against the shared libraries.

I want that too, but ROOT is the wrong place to fix that. The place to fix it is either LLVM or your favorite distribution or both.

@vgvassilev

Copy link
Copy Markdown
Member

Thanks! I tried your patch locally on Ubu26.04 with builtin_llvm=OFF and it looks great.
This will help a lot for Debian packaging.

I would try to keep out the discussion about builtin_llvm=OFF or Debian packaging out of this PR, as this goes way beyond this scope this PR, which is a local improvement to the rootcling options parsing.

But that's the whole point -- now we don't know if we have a second (perhaps incompatible) copy of llvm being randomly loaded as part of your ROOT distribution. I'd argue the opposite: this makes the distribution of ROOT fragile because in one case it will have a single copy of llvm and in the other another one. What becomes now also interesting is when somebody decides to include graphics and libmessa which has it's own copy of llvm. Which one would libmessa pick? What about PyTorch or friends?

If I were you, I'd ask claude to debug this with -trace-expand of cmake and try to push that change into llvm upstream or packaging because ultimately we mix two concepts: static vs dynamic linking. If ROOT has chosen static (in dual mode distribution) llvm should respect that and not add libLLVM.so into the DT_NEEDED elf section.

@ferdymercury

Copy link
Copy Markdown
Collaborator

I want that too, but ROOT is the wrong place to fix that. The place to fix it is either LLVM or your favorite distribution or both.

Ok, I understand your point, but being already a mark_as_advanced flag, I see no harm in having temporary workaround while the issue is fixed in upstream LLVM. Even if they fix it in LLVM today (rather tomorrow), it's going to take 2-3 years until it lands in ROOT, so 6 years until it lands in stable-debian or Ubuntu.

If instead one forces the packagers to fix this, it's triplicating the work: the same hard-coded patches have to be maintained for Debian, Nix, conda, ... ?
So I see it as a gentle solution to provide a (no-warranty) best-effort hidden-behind-advanced-flag option for maintainers for not having to rely on ad-hoc patches, temporarily until this is solved in upstream LLVM.

this makes the distribution of ROOT fragile

And that's ok for the moment in my opinion: since it's behind a hidden no-warranty advanced flag, I do not expect it to work perfectly.
ROOT in Debian, if it ever arrives, will be "experimental". I prefer an experimental minimal version of ROOT in Debian in 1 year from now, to see what needs to be changed/improved, rather than a never-arriving-one until all stuff is sorted out in 10 y.

If ROOT has chosen static (in dual mode distribution) llvm should respect that and not add libLLVM.so into the DT_NEEDED elf section.

In my experimental builds in Debian with system LLVM, I have tried both things:

  • forcing ROOT to choose static linking of llvm: this works fine today and I see no libLLVM.so being mixed (after 4de3462). A grep libLLVM.so showed no results after building/configuring.
  • forcing ROOT to choose dynamic linking of llvm: This only works fine if we add this patch from Jonas. This should be an (advanced-hidden)-opt-in feature if the packager really wants it and takes repsonsibility of not messing up with other libraries with static linking, or maybe even adds a warning when starting ROOT ("do not use pytorch / mesa whatever")

@guitargeek

guitargeek commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

But that's the whole point -- now we don't know if we have a second (perhaps incompatible) copy of llvm being randomly loaded as part of your ROOT distribution.

If that's the whole point, then maybe I see where the confusion comes from! Even with this PR, linking against libLLVM.so is still disabled: for builtin_llvm=OFF, ROOT forces LLVM_LINK_LLVM_DYLIB=OFF and even suppresses find_package(LLVM) calls that would re-enable it (see the TEMPORARY workaround in interpreter/CMakeLists.txt). This PR touches none of that. No build-system or distribution change at all, only rootcling_impl.cxx.

The change that would deserve that scrutiny you have is the future one removing the dylib suppression and supporting LLVM_LINK_LLVM_DYLIB=ON. Happy to have the mesa/PyTorch/second-copy discussion there! But it's too early for that until such a PR exists.

What this PR does is fix an llvm::cl usage defect the idiomatic way: a tool's options belong in their own subcommand, which is what llvm::cl::SubCommand exists for. It's also not a "temporary workaround until LLVM fixes this upstream". There is nothing upstream to fix. LLVM will keep registering -W in its top-level option set, and subcommands are the intended answer.

So: is there any reason to hold this off, besides it being a prerequisite for a later, more controversial change?

vgvassilev added a commit to vgvassilev/root that referenced this pull request Oct 4, 2026
libCling links LLVM and Clang statically and hides them with -Bsymbolic
and libCling.script, so that another LLVM in the process, such as
Triton's (PyTorch's compiler), neither binds to libCling's nor has
libCling bind to its own. Nothing tests this. When the hiding was
incomplete, ALICE saw sporadic crashes with a second libLLVM.so in the
process (root-project#19889), and linking libCling against a shared libLLVM, which
cannot be hidden, would bring them back unnoticed.

Run root.exe and rootcling with a preloaded canary that defines LLVM
functions libCling calls, and fail if one of them is reached; a
self-test checks that the canary catches a library that relies on the
LLVM of its host. With glibc, also fail if the dynamic linker's own log,
LD_DEBUG=bindings, shows a binding of an LLVM or Clang symbol across
libCling's boundary. Check that the canary's names exist in the LLVM
ROOT was built with, so that the test cannot go stale.

The tests are ELF only, and skipped for AddressSanitizer builds, which
control LD_PRELOAD themselves.

Related: the 'Option registered more than once' error with an external
LLVM (root-project#12156), and the discussions in root-project#20903 and root-project#23546 about
rootcling's llvm::cl options and linking against a shared libLLVM.
@ferdymercury

Copy link
Copy Markdown
Collaborator

Side note: when I cherry-picked this into #23372 I saw a build error 'optimize-regalloc' option not found.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants