EnzymeAD / EnzymeAD/Enzyme

hasTerminator() assertion with Rust-generated LLVM IR at -O3 + LTO

Open
#3,177 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.