hasTerminator() assertion with Rust-generated LLVM IR at -O3 + LTO
- Dominant language
- LLVM
- Stars
- 1.7k
- Forks
- 188
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 26
Description
I'm encountering an LLVM `BasicBlock::getTerminator()` assertion while using Enzyme through Rust's experimental autodiff support.
This was originally reported to rust-lang/rust:
https://github.com/rust-lang/rust/issues/160806
The Rust-side discussion suggested that this may need investigation on the Enzyme side, so I'm reporting it here to help determine whether the malformed basic block originates in Enzyme or in the Rust/LLVM integration.
## Error
Compiling the reproducer with `opt-level = 3` and fat LTO results in:
```text
Assertion failed: (hasTerminator() && "cannot get terminator of non-well-formed block"),
function getTerminator, file BasicBlock.h, line 238.
```
The compiler then exits with `SIGABRT`.
## Reproducer
The complete self-contained Rust reproducer and build instructions are available in:
https://github.com/rust-lang/rust/issues/160806
It uses Rust's experimental autodiff support and Enzyme split-mode differentiation (`__enzyme_augmentfwd` / `__enzyme_reverse`).
The relevant release configuration is:
```toml
[profile.release]
opt-level = 3
lto = "fat"
```
and it is built with:
```bash
RUSTFLAGS="-Z autodiff=Enable" cargo +nightly build --release
```
The toolchain used in the original report was:
```text
rustc 1.99.0-nightly (771916f90 2026-08-08)
binary: rustc
commit-hash: 771916f9086dd63cde1b50b2e8bd2c6a9b3078e1
commit-date: 2026-08-08
host: aarch64-apple-darwin
release: 1.99.0-nightly
LLVM version: 21.1.8
```
## Conditions triggering the issue
The crash appears to require the combination of:
- `opt-level = 3`
- fat LTO
- the control-flow pattern in the reproducer
In particular, the relevant code contains a loop with an `if/else`, where one branch can `continue`, followed by mutable slice splitting using `split_at_mut`.
Changing the optimization settings or simplifying the control flow avoids the assertion.
For example, using:
```toml
opt-level = 2
```
does not trigger the crash.
## Question
It looks like `getTerminator()` is being called on a basic block that does not have a terminator.
I'm not sure whether:
1. Enzyme is temporarily or permanently producing a non-well-formed basic block during differentiation,
2. Enzyme is receiving problematic IR from the Rust/LLVM optimization pipeline, or
3. this is an interaction between Enzyme and the optimization/LTO pipeline.
Could you help determine which side is producing the unterminated block?
I'm happy to provide additional LLVM IR, stack traces, or a further reduced reproducer if that would help.
Contributor guide
Research direction
Start with the self-contained Rust reproducer and build instructions in rust-lang/rust#160806, using the shown nightly command with opt-level 3 and fat LTO. Compare the failing configuration with opt-level 2 and collect the additional LLVM IR or stack trace requested in the issue. Done means identifying whether the unterminated basic block comes from Enzyme, Rust/LLVM, or their optimization interaction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100