llvmlite 0.46 breaks optimization #1725

Closed
opened 2026-01-18 19:06:28 +08:00 by Doomsel · 5 comments
Doomsel commented 2026-01-18 19:06:28 +08:00

Migrated from GitHub: #2924


When running the artiq compiler with llvmlite==0.46 (instead of the 0.45 used in the current nix environment), the python process crashes with the exception below.
To reproduce run an arbitrary experiment with artiq_run.

Invalid optimization level for size level 2
UNREACHABLE executed at /tmp/pip-install-34zfw1k3/llvmlite_3325acc1594f407ebffc2ad8ceb9ded2/ffi/newpassmanagers.cpp:286!
Fatal Python error: Aborted

Current thread 0x00007f891f0f5b28 (most recent call first):
  File "/usr/lib/python3.12/site-packages/llvmlite/binding/ffi.py", line 212 in __call__
  File "/usr/lib/python3.12/site-packages/llvmlite/binding/newpassmanagers.py", line 585 in getModulePassManager
  File "/git/artiq/artiq/compiler/targets.py", line 120 in optimize
  File "/git/artiq/artiq/compiler/targets.py", line 151 in compile
  File "/git/artiq/artiq/compiler/targets.py", line 188 in compile_and_link
  File "/git/artiq/artiq/coredevice/core.py", line 150 in compile
  File "/git/artiq/artiq/coredevice/core.py", line 175 in run
  File "/git/artiq/artiq/language/core.py", line 54 in run_on_core

If i change the size optimization level here to size_level=0 the code compiles without issue. The same goes for when changing the speed level to 2 (or commenting out the line).

If one sets the speed level directly in line 111:

pto = llvm.create_pipeline_tuning_options(speed_level=1, size_level=1)

It directly complains:

self = <llvmlite.binding.newpassmanagers.PipelineTuningOptions object at 0x7f3373ff8d70>, value = 1

    @size_level.setter
    def size_level(self, value):
        if not 0 <= value <= 2:
            raise ValueError("Optimization level for size should be 0, 1, or 2")
        if value != 0 and self.speed_level != 2:
>           raise ValueError(
                "Optimization for size should be encoded with speed level == 2")
E           ValueError: Optimization for size should be encoded with speed level == 2

/usr/lib/python3.12/site-packages/llvmlite/binding/newpassmanagers.py:519: ValueError

Seems like transitioning to the NewPassManager is still ongoing in llvmlite...

> **Migrated from GitHub:** [#2924](https://github.com/m-labs/artiq/issues/2924) --- When running the artiq compiler with llvmlite==0.46 (instead of the 0.45 used in the current nix environment), the python process crashes with the exception below. To reproduce run an arbitrary experiment with `artiq_run`. ``` Invalid optimization level for size level 2 UNREACHABLE executed at /tmp/pip-install-34zfw1k3/llvmlite_3325acc1594f407ebffc2ad8ceb9ded2/ffi/newpassmanagers.cpp:286! Fatal Python error: Aborted Current thread 0x00007f891f0f5b28 (most recent call first): File "/usr/lib/python3.12/site-packages/llvmlite/binding/ffi.py", line 212 in __call__ File "/usr/lib/python3.12/site-packages/llvmlite/binding/newpassmanagers.py", line 585 in getModulePassManager File "/git/artiq/artiq/compiler/targets.py", line 120 in optimize File "/git/artiq/artiq/compiler/targets.py", line 151 in compile File "/git/artiq/artiq/compiler/targets.py", line 188 in compile_and_link File "/git/artiq/artiq/coredevice/core.py", line 150 in compile File "/git/artiq/artiq/coredevice/core.py", line 175 in run File "/git/artiq/artiq/language/core.py", line 54 in run_on_core ``` If i change the size optimization level [here](https://github.com/m-labs/artiq/blob/c2f17af93abae2b72cc6b5a12d9fcd2d784cdfc7/artiq/compiler/targets.py#L111) to `size_level=0` the code compiles without issue. The same goes for when changing the [speed level ](https://github.com/m-labs/artiq/blob/c2f17af93abae2b72cc6b5a12d9fcd2d784cdfc7/artiq/compiler/targets.py#L117) to `2` (or commenting out the line). If one sets the speed level directly in line 111: ``` pto = llvm.create_pipeline_tuning_options(speed_level=1, size_level=1) ``` It directly complains: ``` self = <llvmlite.binding.newpassmanagers.PipelineTuningOptions object at 0x7f3373ff8d70>, value = 1 @size_level.setter def size_level(self, value): if not 0 <= value <= 2: raise ValueError("Optimization level for size should be 0, 1, or 2") if value != 0 and self.speed_level != 2: > raise ValueError( "Optimization for size should be encoded with speed level == 2") E ValueError: Optimization for size should be encoded with speed level == 2 /usr/lib/python3.12/site-packages/llvmlite/binding/newpassmanagers.py:519: ValueError ``` Seems like transitioning to the NewPassManager is still ongoing in llvmlite...
sb10q closed this issue 2026-01-18 19:06:28 +08:00
Contributor

This is not related to the new pass manager, but https://github.com/numba/llvmlite/issues/1306.

This is not related to the new pass manager, but https://github.com/numba/llvmlite/issues/1306.
Contributor
See also https://github.com/numba/llvmlite/pull/1321.
Contributor

https://github.com/numba/llvmlite/pull/1321#issuecomment-3384040409

As upstream thinks that "adding backward compatibility for this might not be ideal" (?!), we'll have to work around this somehow. One way would be to explicitly check for the llvmlite version (or just switch then arguments when updating the dependency in the flake).

https://github.com/numba/llvmlite/pull/1321#issuecomment-3384040409 As upstream thinks that "adding backward compatibility for this might not be ideal" (?!), we'll have to work around this somehow. One way would be to explicitly check for the llvmlite version (or just switch then arguments when updating the dependency in the flake).
Doomsel commented 2026-01-18 19:06:29 +08:00

Perfect, I see you are already aware of the issue. Quite a bold move to deprecate within one minor version...
I would be in favor of checking the llvmlite version either explicitly or pinning the llvmlite version this would probably make the transitioning time smoother.

Perfect, I see you are already aware of the issue. Quite a bold move to deprecate within one minor version... I would be in favor of checking the llvmlite version either explicitly or pinning the llvmlite version this would probably make the transitioning time smoother.
Contributor

Yep – I understand that the Numba team may be primarily interested in maintaining llvmlite for Numba's sake, and they do not appear to use size_level. Still, changing the API in a mutually incompatible way that leads to C++-side crashes rather than exceptions that could be caught is not ideal.

As for me being aware of the issue, that is true, but I'm not actively working on the ARTIQ side of this right now – a PR would be very much appreciated! :) Generally, we don't seamlessly support multiple llvmlite versions (there tends to be breakage filtering through from the LLVM side, if nothing else), but if that's the only thing breaking 0.46 compatibility, it seems worth it to me.

Yep – I understand that the Numba team may be primarily interested in maintaining llvmlite for Numba's sake, and they do not appear to use `size_level`. Still, changing the API in a mutually incompatible way that leads to C++-side crashes rather than exceptions that could be caught is not ideal. As for me being aware of the issue, that is true, but I'm not actively working on the ARTIQ side of this right now – a PR would be very much appreciated! :) Generally, we don't seamlessly support multiple llvmlite versions (there tends to be breakage filtering through from the LLVM side, if nothing else), but if that's the only thing breaking 0.46 compatibility, it seems worth it to me.
Sign in to join this conversation.