[SelectionDAG] mergeTruncStores merges into an illegal i16 after type legalization ("Unexpected illegal type!" / "Cannot select")
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
`llc` fails on the following IR for AArch64 and WebAssembly:
```llvm
define void @func(i64 %0, ptr %p, ptr noalias %a) {
%b = load ptr, ptr null, align 8
%c = lshr i64 %0, 8
%d = trunc i64 %c to i8
store i8 %d, ptr %p, align 1
%e = load i8, ptr %p, align 1
store i8 %e, ptr %b, align 1
%f = trunc i64 %0 to i8
store i8 %f, ptr %a, align 1
%h = load ptr, ptr null, align 8
%i = getelementptr i8, ptr %h, i64 1
%j = load i8, ptr %a, align 1
store i8 %j, ptr %i, align 1
store i8 0, ptr %p, align 1
ret void
}
```
Compiler Explorer: https://godbolt.org/z/Mnr7Yvz4T
There are two symptoms depending on the build.
**With assertions**, an illegal `i16` reaches `SelectionDAGLegalize::LegalizeOp`:
```console
$ llc -mtriple=aarch64 repro.ll -o /dev/null
Assertion failed: ((TLI.getTypeAction(*DAG.getContext(), Op.getValueType()) == TargetLowering::TypeLegal || Op.getOpcode() == ISD::TargetConstant || Op.getOpcode() == ISD::Register) && "Unexpected illegal type!"), function LegalizeOp, file LegalizeDAG.cpp, line 1001.
...
#8 (anonymous namespace)::SelectionDAGLegalize::LegalizeOp(llvm::SDNode*)
#9 llvm::SelectionDAG::Legalize()
#10 llvm::SelectionDAGISel::CodeGenAndEmitDAG()
```
**Without assertions** the illegal nodes survive legalization and reach instruction selection, which aborts:
```console
LLVM ERROR: Cannot select: ch = store<(store (s16) into %ir.b, align 1)> t.., t.., t.., poison:i64
i16 = bswap t..
i16 = truncate t..
i64,ch = CopyFromReg t.., Register:i64 %0
i64,ch = load<(load (s64) from `ptr null`)> t.., Constant:i64<0>, poison:i64
In function: func
...
#7 llvm::SelectionDAGISel::CannotYetSelect(llvm::SDNode*)
#8 llvm::SelectionDAGISel::SelectCodeCommon(...)
#9 (anonymous namespace)::AArch64DAGToDAGISel::Select(llvm::SDNode*)
```
## Affected targets
Reproduces on `aarch64`, `aarch64_32`, `arm64`, `arm64_32`, and on `wasm32`, `wasm64`, targets on which `i16` is not a legal type. It does *not* reproduce on `aarch64_be`, nor on any of the other 37 targets I tried (arm, armeb, thumb, thumbeb, x86_64, i386, riscv32/64 (+be), systemz, ppc32/64(le), mips(el), mips64(el), hexagon, ve, loongarch32/64, sparc(el), sparcv9, avr, msp430, bpf(eb/el), nvptx(64), lanai, spirv32/64).
Needs `-O1` or higher; at `-O0` the fold is skipped and the IR compiles fine.
## Versions
Reproduces with `llc` 15.0.0 through 23.1.0 and current trunk. `llc` 14 and earlier reject the opaque-pointer syntax.
## Cause
`DAGCombiner::mergeTruncStores()` creates a `TRUNCATE` to the merged wide type plus, for reversed byte order, a `BSWAP`/`ROTR` of it, which is only valid before type legalization. The guard tests `LegalOperations`, which is only set from the `AfterLegalizeVectorOps` combine round onwards, so the fold also runs in the `AfterLegalizeTypes` round, where every node must already have a legal type. Here two `i8` stores are merged into an `i16` store; `i16` is illegal on AArch64 and WebAssembly, so the `i16 bswap` survives as an operand of the merged store.
I have a fix for this and will open a PR shortly.
Contributor guide
Research direction
Start at DAGCombiner::mergeTruncStores() and inspect the LegalOperations guard against the AfterLegalizeTypes combine round. Reproduce with llc -mtriple=aarch64 on the supplied IR at -O1, then verify that the illegal i16 no longer reaches SelectionDAGLegalize::LegalizeOp or instruction selection on AArch64 and WebAssembly.
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
- Clearly specified
- Newbie friendliness
- 30/100