llvm / llvm/llvm-project

[X86][GlobalISel] Cannot select G_UNMERGE_VALUES/G_MERGE_VALUES for i64 crossing a multi-block PHI on i686

Open
#216,648 3 comments 0 reactions 0 assignees View on GitHub
backend:X86 confirmed crash-on-valid llvm:globalisel
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Description

`llc -mtriple=i686-* -global-isel` aborts with "cannot select" on ordinary IR that merges an `i64` value via a PHI across basic blocks. No exotic IR constructs are involved — just a simple diamond CFG returning one of two `i64` values.

## Reproducer

```llvm
define i64 @plain_phi_i64(i64 %x, i64 %y, i1 %c) {
entry:
br i1 %c, label %a, label %b
a:
br label %tail
b:
br label %tail
tail:
%r = phi i64 [ %x, %a ], [ %y, %b ]
ret i64 %r
}
```

```
$ llc -O0 -mtriple=i686-unknown-linux-gnu -global-isel repro.ll -o -
LLVM ERROR: cannot select: %14:gpr(s32), %15:gpr(s32) = G_UNMERGE_VALUES %13:gpr(s64) (in function: plain_phi_i64)
```

A related shape (a loop-carried `i64` PHI feeding a fresh `G_MERGE_VALUES` each iteration — e.g. reachable after the legalizer's `NarrowScalar` action splits an i64 `and`/`or`/`xor`/`add`/`sub` on i686 down to two i32 halves) fails the same way with a different missing pattern:

```llvm
define i64 @or_xor_loop(i64 %x, i64 %y, i32 %n) {
entry:
br label %loop
loop:
%acc = phi i64 [ 0, %entry ], [ %next, %loop ]
%i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
%o = or i64 %acc, %x
%next = xor i64 %o, %y
%i.next = add i32 %i, 1
%cond = icmp slt i32 %i.next, %n
br i1 %cond, label %loop, label %exit
exit:
ret i64 %next
}
```

```
$ llc -O0 -mtriple=i686-unknown-linux-gnu -global-isel repro2.ll -o -
LLVM ERROR: cannot select: %15:gpr(s64) = G_MERGE_VALUES %28:gpr(s32), %29:gpr(s32) (in function: or_xor_loop)
```

## Analysis

- Straight-line (single basic block) `i64` arithmetic on i686 GlobalISel works fine — e.g. `test_mul_i64` in `llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll` passes today. The gap is specific to an s64 value flowing through a multi-block `G_PHI` before being unmerged/merged.
- I could not find any existing test under `llvm/test/CodeGen/X86/GlobalISel/` that exercises full-pipeline (i.e. not `-stop-after=`) `-global-isel` codegen on `i686-*` with any branch or PHI at all, so this may be a known/accepted gap in 32-bit x86 GlobalISel's control-flow coverage rather than a fresh regression.
- With graceful fallback (`-global-isel-abort=0`, the mode any non-testing invocation would realistically use), this silently and correctly falls back to SelectionDAG for the affected function — no user-visible failure. The hard abort above only reproduces under strict abort-on-failure mode, which happens to be `llc -global-isel`'s default.

Filing this as a heads-up for anyone working on expanding GlobalISel coverage for 32-bit X86 in the future, not as an urgent/user-facing issue — I don't believe this affects any real user today given the two points above.

## Environment

Reproduced at commit 513a02ee150335a84c39ab2e2d0f965f1b0e260e (llvm-project `main`). Confirmed via `git diff` that `llvm/lib/Target/X86/GISel/` and `llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp` are unmodified relative to that commit in the branch this was found on, so this should reproduce unmodified on upstream `main`.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing both failures with the supplied llc commands, then inspect llvm/lib/Target/X86/GISel/ and llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp. Compare the case with llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll and identify the missing multi-block PHI merge/unmerge coverage. Done means i686 full-pipeline GlobalISel handles these shapes and has regression tests.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.