[llvm] [X86][NFC] Pre-commit tests for trunc(select) narrowing (PR #223762)

Paweł Bylica via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 15 10:10:05 PDT 2026


https://github.com/chfast created https://github.com/llvm/llvm-project/pull/223762

DAGCombiner narrows trunc(select c, a, b) to the truncated type. On X86 that
gives an i8 select, which is widened back to i32 wherever CMOV is available,
so the narrowing only adds a byte ALU chain and a MOVZX.


Assisted-by: Claude Code


>From e694143367b8dd829cf7c04d29791d0550d579d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Tue, 15 Sep 2026 18:57:16 +0200
Subject: [PATCH] [X86][NFC] Pre-commit tests for trunc(select) narrowing

DAGCombiner narrows trunc(select c, a, b) to the truncated type. On X86 that
gives an i8 select, which is widened back to i32 wherever CMOV is available,
so the narrowing only adds a byte ALU chain and a MOVZX.

Assisted-by: Claude Code
---
 .../CodeGen/X86/trunc-select-narrowing.ll     | 239 ++++++++++++++++++
 1 file changed, 239 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/trunc-select-narrowing.ll

diff --git a/llvm/test/CodeGen/X86/trunc-select-narrowing.ll b/llvm/test/CodeGen/X86/trunc-select-narrowing.ll
new file mode 100644
index 0000000000000..73dffa7ef1848
--- /dev/null
+++ b/llvm/test/CodeGen/X86/trunc-select-narrowing.ll
@@ -0,0 +1,239 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s --check-prefixes=X64
+; RUN: llc < %s -mtriple=i686-- -mattr=+cmov | FileCheck %s --check-prefixes=X86-CMOV
+; RUN: llc < %s -mtriple=i686-- -mattr=-cmov | FileCheck %s --check-prefixes=X86-NOCMOV
+
+; DAGCombiner narrows trunc(select c, a, b) to the truncated type. X86 has no
+; 8-bit CMOV, so where CMOV is available an i8 select is widened back to i32
+; during legalization and the narrowing only adds MOVZX. Check that the select
+; stays wide there, and that it is still narrowed without CMOV, where the select
+; becomes a branch instead.
+
+define i8 @trunc_select_add_i64(i64 %x, i64 %y) nounwind {
+; X64-LABEL: trunc_select_add_i64:
+; X64:       # %bb.0:
+; X64-NEXT:    bsrq %rdi, %rcx
+; X64-NEXT:    xorl $63, %ecx
+; X64-NEXT:    bsrq %rsi, %rax
+; X64-NEXT:    xorl $63, %eax
+; X64-NEXT:    orb $64, %al
+; X64-NEXT:    testq %rdi, %rdi
+; X64-NEXT:    movzbl %al, %eax
+; X64-NEXT:    cmovnel %ecx, %eax
+; X64-NEXT:    # kill: def $al killed $al killed $eax
+; X64-NEXT:    retq
+;
+; X86-CMOV-LABEL: trunc_select_add_i64:
+; X86-CMOV:       # %bb.0:
+; X86-CMOV-NEXT:    pushl %edi
+; X86-CMOV-NEXT:    pushl %esi
+; X86-CMOV-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-CMOV-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-CMOV-NEXT:    movl {{[0-9]+}}(%esp), %esi
+; X86-CMOV-NEXT:    bsrl %esi, %edi
+; X86-CMOV-NEXT:    xorl $31, %edi
+; X86-CMOV-NEXT:    bsrl {{[0-9]+}}(%esp), %eax
+; X86-CMOV-NEXT:    xorl $31, %eax
+; X86-CMOV-NEXT:    orl $32, %eax
+; X86-CMOV-NEXT:    testl %esi, %esi
+; X86-CMOV-NEXT:    cmovnel %edi, %eax
+; X86-CMOV-NEXT:    movl %edx, %esi
+; X86-CMOV-NEXT:    orl %ecx, %esi
+; X86-CMOV-NEXT:    je .LBB0_1
+; X86-CMOV-NEXT:  # %bb.2: # %select.true.sink
+; X86-CMOV-NEXT:    bsrl %ecx, %esi
+; X86-CMOV-NEXT:    xorl $31, %esi
+; X86-CMOV-NEXT:    bsrl %edx, %eax
+; X86-CMOV-NEXT:    xorl $31, %eax
+; X86-CMOV-NEXT:    orl $32, %eax
+; X86-CMOV-NEXT:    testl %ecx, %ecx
+; X86-CMOV-NEXT:    cmovnel %esi, %eax
+; X86-CMOV-NEXT:    jmp .LBB0_3
+; X86-CMOV-NEXT:  .LBB0_1:
+; X86-CMOV-NEXT:    orl $64, %eax
+; X86-CMOV-NEXT:  .LBB0_3: # %select.end
+; X86-CMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-CMOV-NEXT:    popl %esi
+; X86-CMOV-NEXT:    popl %edi
+; X86-CMOV-NEXT:    retl
+;
+; X86-NOCMOV-LABEL: trunc_select_add_i64:
+; X86-NOCMOV:       # %bb.0:
+; X86-NOCMOV-NEXT:    pushl %esi
+; X86-NOCMOV-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NOCMOV-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; X86-NOCMOV-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NOCMOV-NEXT:    testl %eax, %eax
+; X86-NOCMOV-NEXT:    jne .LBB0_2
+; X86-NOCMOV-NEXT:  # %bb.1:
+; X86-NOCMOV-NEXT:    bsrl {{[0-9]+}}(%esp), %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:    orl $32, %eax
+; X86-NOCMOV-NEXT:    jmp .LBB0_3
+; X86-NOCMOV-NEXT:  .LBB0_2:
+; X86-NOCMOV-NEXT:    bsrl %eax, %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:  .LBB0_3:
+; X86-NOCMOV-NEXT:    movl %edx, %esi
+; X86-NOCMOV-NEXT:    orl %ecx, %esi
+; X86-NOCMOV-NEXT:    je .LBB0_6
+; X86-NOCMOV-NEXT:  # %bb.4: # %select.true.sink
+; X86-NOCMOV-NEXT:    testl %ecx, %ecx
+; X86-NOCMOV-NEXT:    jne .LBB0_8
+; X86-NOCMOV-NEXT:  # %bb.5: # %select.true.sink
+; X86-NOCMOV-NEXT:    bsrl %edx, %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:    orl $32, %eax
+; X86-NOCMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-NOCMOV-NEXT:    popl %esi
+; X86-NOCMOV-NEXT:    retl
+; X86-NOCMOV-NEXT:  .LBB0_6:
+; X86-NOCMOV-NEXT:    orl $64, %eax
+; X86-NOCMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-NOCMOV-NEXT:    popl %esi
+; X86-NOCMOV-NEXT:    retl
+; X86-NOCMOV-NEXT:  .LBB0_8:
+; X86-NOCMOV-NEXT:    bsrl %ecx, %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-NOCMOV-NEXT:    popl %esi
+; X86-NOCMOV-NEXT:    retl
+  %hi = call i64 @llvm.ctlz.i64(i64 %x, i1 true)
+  %lo = call i64 @llvm.ctlz.i64(i64 %y, i1 true)
+  %a = add i64 %lo, 64
+  %c = icmp ne i64 %x, 0
+  %s = select i1 %c, i64 %hi, i64 %a
+  %t = trunc i64 %s to i8
+  ret i8 %t
+}
+
+define i8 @trunc_select_add_i32(i32 %x, i32 %y) nounwind {
+; X64-LABEL: trunc_select_add_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    bsrl %edi, %ecx
+; X64-NEXT:    xorl $31, %ecx
+; X64-NEXT:    bsrl %esi, %eax
+; X64-NEXT:    xorl $31, %eax
+; X64-NEXT:    orb $32, %al
+; X64-NEXT:    testl %edi, %edi
+; X64-NEXT:    movzbl %al, %eax
+; X64-NEXT:    cmovnel %ecx, %eax
+; X64-NEXT:    # kill: def $al killed $al killed $eax
+; X64-NEXT:    retq
+;
+; X86-CMOV-LABEL: trunc_select_add_i32:
+; X86-CMOV:       # %bb.0:
+; X86-CMOV-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-CMOV-NEXT:    bsrl %eax, %ecx
+; X86-CMOV-NEXT:    xorl $31, %ecx
+; X86-CMOV-NEXT:    bsrl {{[0-9]+}}(%esp), %edx
+; X86-CMOV-NEXT:    xorl $31, %edx
+; X86-CMOV-NEXT:    orb $32, %dl
+; X86-CMOV-NEXT:    testl %eax, %eax
+; X86-CMOV-NEXT:    movzbl %dl, %eax
+; X86-CMOV-NEXT:    cmovnel %ecx, %eax
+; X86-CMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-CMOV-NEXT:    retl
+;
+; X86-NOCMOV-LABEL: trunc_select_add_i32:
+; X86-NOCMOV:       # %bb.0:
+; X86-NOCMOV-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-NOCMOV-NEXT:    testl %eax, %eax
+; X86-NOCMOV-NEXT:    jne .LBB1_1
+; X86-NOCMOV-NEXT:  # %bb.2:
+; X86-NOCMOV-NEXT:    bsrl {{[0-9]+}}(%esp), %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:    orb $32, %al
+; X86-NOCMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-NOCMOV-NEXT:    retl
+; X86-NOCMOV-NEXT:  .LBB1_1:
+; X86-NOCMOV-NEXT:    bsrl %eax, %eax
+; X86-NOCMOV-NEXT:    xorl $31, %eax
+; X86-NOCMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-NOCMOV-NEXT:    retl
+  %hi = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
+  %lo = call i32 @llvm.ctlz.i32(i32 %y, i1 true)
+  %a = add i32 %lo, 32
+  %c = icmp ne i32 %x, 0
+  %s = select i1 %c, i32 %hi, i32 %a
+  %t = trunc i32 %s to i8
+  ret i8 %t
+}
+
+; A select of two constants is still narrowed: no truncate of a computed value
+; is introduced, so the narrow select is never worse.
+
+define i8 @trunc_select_constants(i32 %x) nounwind {
+; X64-LABEL: trunc_select_constants:
+; X64:       # %bb.0:
+; X64-NEXT:    testl %edi, %edi
+; X64-NEXT:    sete %al
+; X64-NEXT:    shlb $2, %al
+; X64-NEXT:    addb $7, %al
+; X64-NEXT:    retq
+;
+; X86-CMOV-LABEL: trunc_select_constants:
+; X86-CMOV:       # %bb.0:
+; X86-CMOV-NEXT:    cmpl $0, {{[0-9]+}}(%esp)
+; X86-CMOV-NEXT:    sete %al
+; X86-CMOV-NEXT:    shlb $2, %al
+; X86-CMOV-NEXT:    addb $7, %al
+; X86-CMOV-NEXT:    retl
+;
+; X86-NOCMOV-LABEL: trunc_select_constants:
+; X86-NOCMOV:       # %bb.0:
+; X86-NOCMOV-NEXT:    cmpl $0, {{[0-9]+}}(%esp)
+; X86-NOCMOV-NEXT:    sete %al
+; X86-NOCMOV-NEXT:    shlb $2, %al
+; X86-NOCMOV-NEXT:    addb $7, %al
+; X86-NOCMOV-NEXT:    retl
+  %c = icmp ne i32 %x, 0
+  %s = select i1 %c, i32 7, i32 11
+  %t = trunc i32 %s to i8
+  ret i8 %t
+}
+
+; Only one arm is constant, so the exemption does not apply.
+
+define i8 @trunc_select_one_constant(i32 %x, i32 %y) nounwind {
+; X64-LABEL: trunc_select_one_constant:
+; X64:       # %bb.0:
+; X64-NEXT:    orb $64, %sil
+; X64-NEXT:    testl %edi, %edi
+; X64-NEXT:    movzbl %sil, %ecx
+; X64-NEXT:    movl $11, %eax
+; X64-NEXT:    cmovnel %ecx, %eax
+; X64-NEXT:    # kill: def $al killed $al killed $eax
+; X64-NEXT:    retq
+;
+; X86-CMOV-LABEL: trunc_select_one_constant:
+; X86-CMOV:       # %bb.0:
+; X86-CMOV-NEXT:    movzbl {{[0-9]+}}(%esp), %eax
+; X86-CMOV-NEXT:    orb $64, %al
+; X86-CMOV-NEXT:    cmpl $0, {{[0-9]+}}(%esp)
+; X86-CMOV-NEXT:    movzbl %al, %ecx
+; X86-CMOV-NEXT:    movl $11, %eax
+; X86-CMOV-NEXT:    cmovnel %ecx, %eax
+; X86-CMOV-NEXT:    # kill: def $al killed $al killed $eax
+; X86-CMOV-NEXT:    retl
+;
+; X86-NOCMOV-LABEL: trunc_select_one_constant:
+; X86-NOCMOV:       # %bb.0:
+; X86-NOCMOV-NEXT:    cmpl $0, {{[0-9]+}}(%esp)
+; X86-NOCMOV-NEXT:    jne .LBB3_1
+; X86-NOCMOV-NEXT:  # %bb.2:
+; X86-NOCMOV-NEXT:    movb $11, %al
+; X86-NOCMOV-NEXT:    retl
+; X86-NOCMOV-NEXT:  .LBB3_1:
+; X86-NOCMOV-NEXT:    movzbl {{[0-9]+}}(%esp), %eax
+; X86-NOCMOV-NEXT:    orb $64, %al
+; X86-NOCMOV-NEXT:    retl
+  %o = or i32 %y, 64
+  %c = icmp ne i32 %x, 0
+  %s = select i1 %c, i32 %o, i32 11
+  %t = trunc i32 %s to i8
+  ret i8 %t
+}
+
+declare i64 @llvm.ctlz.i64(i64, i1)
+declare i32 @llvm.ctlz.i32(i32, i1)



More information about the llvm-commits mailing list