llvm / llvm/llvm-project

[AArch64][SME] AlwaysInliner's new attribute-compatibility check (#209345) leaves un-legalizable orphan functions instead of preventing them

Open
#217,639 9 comments 0 reactions 0 assignees View on GitHub
backend:AArch64 confirmed crash-on-valid llvm:transforms
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

`[AlwaysInliner] Do not inline on attribute mismatches` (#209345, commit 37b8e765ce48) made `AlwaysInlinerPass` consult `TTI::areInlineCompatible` before inlining an `alwaysinline` callee. On AArch64, this now blocks inlining a plain (non-streaming-attributed) helper that only contains SVE intrinsics into a caller with `"aarch64_pstate_sm_body"`, because `AArch64TTIImpl::areInlineCompatible` → `hasPossibleIncompatibleOps` conservatively flags *any* `@llvm.aarch64.*` intrinsic call as SM-incompatible.

The callee doesn't get an alternative, legal lowering — it's `alwaysinline` precisely because it has none. Leaving it un-inlined just relocates the bug: the orphaned function has no streaming/SVE attribute of its own, so `AArch64Subtarget::isSVEorStreamingSVEAvailable()` is false for it, and instruction selection later crashes:

```
fatal error: error in backend: Don't know how to legalize this scalable vector type
...
3. Running pass 'AArch64 Instruction Selection' on function '@fast_inverse_f16nx8'
```

**Minimal reproducer** (no frontend needed):

```llvm
target triple = "aarch64-unknown-linux-gnu"

define linkonce_odr @fast_inverse_f16nx8( %x) #0 {
%approx = tail call @llvm.aarch64.sve.frecpe.x.nxv8f16( %x)
%correction = tail call @llvm.aarch64.sve.frecps.x.nxv8f16( %approx, %x)
%result = fmul %approx, %correction
ret %result
}

declare @llvm.aarch64.sve.frecpe.x.nxv8f16() #1
declare @llvm.aarch64.sve.frecps.x.nxv8f16(, ) #1

define @caller( %v) #2 {
%r = call @fast_inverse_f16nx8( %v)
ret %r
}

attributes #0 = { alwaysinline mustprogress nounwind "target-cpu"="generic" "target-features"="+fullfp16,+sme2" }
attributes #1 = { nocallback nofree nosync nounwind willreturn memory(none) }
attributes #2 = { mustprogress noinline nounwind vscale_range(4,4) "aarch64_pstate_sm_body" "target-cpu"="generic" "target-features"="+fullfp16,+sme2" }
```

```
$ clang -c repro.ll -O2 -o repro.o
```

- Before 37b8e765ce48: `@fast_inverse_f16nx8` is inlined into `@caller` by `always-inline`; compiles cleanly.
- At/after 37b8e765ce48 (reproduced on `23.1.0rc3`): inlining is skipped; compilation crashes as above.

Found via Halide, which emits exactly this pattern (an `alwaysinline` runtime helper built from SVE-only intrinsics, called from an SME2-streaming-mode function) for its ARM SVE2/SME2 backend.

Possible fixes: have `hasPossibleIncompatibleOps` not treat streaming-compatible-only SVE intrinsics as incompatible, or have `AlwaysInlinerPass` force the inline (emitting the diagnostic but still inlining) when the callee is `alwaysinline` and has no other legal target.

Contributor guide

Open the contributing guide

Research direction

Start by running the provided repro.ll with clang -c repro.ll -O2 and inspect AlwaysInlinerPass, AArch64TTIImpl::areInlineCompatible, and hasPossibleIncompatibleOps. Trace how AArch64Subtarget::isSVEorStreamingSVEAvailable() affects the orphan function; done means the reproducer no longer reaches the scalable-vector legalization crash while preserving the intended compatibility checks.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.