[llvm] [X86] Prevent NUW flag forwarding in ADD->SUB peephole (PR #201612)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 09:34:36 PDT 2026
https://github.com/hesam-oxe updated https://github.com/llvm/llvm-project/pull/201612
>From f9567f7aa603f4d8de0946955eac53d4212b273e Mon Sep 17 00:00:00 2001
From: hesam-oxe <chngyzkhanwhsht at gmail.com>
Date: Thu, 4 Jun 2026 19:05:06 +0330
Subject: [PATCH 1/2] [X86] Prevent NUW flag forwarding in ADD->SUB peephole
Added add_without_nuw PatFrag and updated all ADD->SUB patterns. Fixes #160217
---
llvm/lib/Target/X86/X86InstrCompiler.td | 41 +++++++++++++++----------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index b228f4deb704e..41fbdfde20874 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1598,6 +1598,13 @@ def : Pat<(xor GR16:$src1, -32768),
def : Pat<(xor GR32:$src1, -2147483648),
(ADD32ri GR32:$src1, -2147483648)>;
}
+// Match an `add` that does NOT have the NUW (No Unsigned Wrap) flag.
+// This prevents the NUW flag from being incorrectly forwarded to SUB
+// instructions when we convert `add X, 128` to `SUB X, -128`.
+def add_without_nuw : PatFrag<(ops node:$src1, node:$src2),
+ (add node:$src1, node:$src2), [{
+ return !N->getFlags().hasNoUnsignedWrap();
+}]>;
//===----------------------------------------------------------------------===//
// Some peepholes
@@ -1606,9 +1613,9 @@ def : Pat<(xor GR32:$src1, -2147483648),
// Odd encoding trick: -128 fits into an 8-bit immediate field while
// +128 doesn't, so in this special case use a sub instead of an add.
let Predicates = [NoNDD] in {
- def : Pat<(add GR16:$src1, 128),
+ def : Pat<(add_without_nuw GR16:$src1, 128),
(SUB16ri GR16:$src1, -128)>;
- def : Pat<(add GR32:$src1, 128),
+ def : Pat<(add_without_nuw GR32:$src1, 128),
(SUB32ri GR32:$src1, -128)>;
def : Pat<(X86add_flag_nocf GR16:$src1, 128),
@@ -1617,15 +1624,15 @@ let Predicates = [NoNDD] in {
(SUB32ri GR32:$src1, -128)>;
}
let Predicates = [NoNDDI] in {
- def : Pat<(add GR64:$src1, 128),
+ def : Pat<(add_without_nuw GR64:$src1, 128),
(SUB64ri32 GR64:$src1, -128)>;
def : Pat<(X86add_flag_nocf GR64:$src1, 128),
(SUB64ri32 GR64:$src1, -128)>;
}
let Predicates = [HasNDD] in {
- def : Pat<(add GR16:$src1, 128),
+ def : Pat<(add_without_nuw GR16:$src1, 128),
(SUB16ri_ND GR16:$src1, -128)>;
- def : Pat<(add GR32:$src1, 128),
+ def : Pat<(add_without_nuw GR32:$src1, 128),
(SUB32ri_ND GR32:$src1, -128)>;
def : Pat<(X86add_flag_nocf GR16:$src1, 128),
@@ -1634,44 +1641,44 @@ let Predicates = [HasNDD] in {
(SUB32ri_ND GR32:$src1, -128)>;
}
let Predicates = [HasNDDI] in {
- def : Pat<(add GR64:$src1, 128),
+ def : Pat<(add_without_nuw GR64:$src1, 128),
(SUB64ri32_ND GR64:$src1, -128)>;
def : Pat<(X86add_flag_nocf GR64:$src1, 128),
(SUB64ri32_ND GR64:$src1, -128)>;
}
-def : Pat<(store (add (loadi16 addr:$dst), 128), addr:$dst),
+def : Pat<(store (add_without_nuw (loadi16 addr:$dst), 128), addr:$dst),
(SUB16mi addr:$dst, -128)>;
-def : Pat<(store (add (loadi32 addr:$dst), 128), addr:$dst),
+def : Pat<(store (add_without_nuw (loadi32 addr:$dst), 128), addr:$dst),
(SUB32mi addr:$dst, -128)>;
-def : Pat<(store (add (loadi64 addr:$dst), 128), addr:$dst),
+def : Pat<(store (add_without_nuw (loadi64 addr:$dst), 128), addr:$dst),
(SUB64mi32 addr:$dst, -128)>;
let Predicates = [HasNDD] in {
- def : Pat<(add (loadi16 ndd_addr:$src), 128),
+ def : Pat<(add_without_nuw (loadi16 ndd_addr:$src), 128),
(SUB16mi_ND ndd_addr:$src, -128)>;
- def : Pat<(add (loadi32 ndd_addr:$src), 128),
+ def : Pat<(add_without_nuw (loadi32 ndd_addr:$src), 128),
(SUB32mi_ND ndd_addr:$src, -128)>;
- def : Pat<(add (loadi64 ndd_addr:$src), 128),
+ def : Pat<(add_without_nuw (loadi64 ndd_addr:$src), 128),
(SUB64mi32_ND ndd_addr:$src, -128)>;
}
// The same trick applies for 32-bit immediate fields in 64-bit
// instructions.
let Predicates = [NoNDD] in {
- def : Pat<(add GR64:$src1, 0x0000000080000000),
+ def : Pat<(add_without_nuw GR64:$src1, 0x0000000080000000),
(SUB64ri32 GR64:$src1, 0xffffffff80000000)>;
def : Pat<(X86add_flag_nocf GR64:$src1, 0x0000000080000000),
(SUB64ri32 GR64:$src1, 0xffffffff80000000)>;
}
let Predicates = [HasNDDI] in {
- def : Pat<(add GR64:$src1, 0x0000000080000000),
+ def : Pat<(add_without_nuw GR64:$src1, 0x0000000080000000),
(SUB64ri32_ND GR64:$src1, 0xffffffff80000000)>;
def : Pat<(X86add_flag_nocf GR64:$src1, 0x0000000080000000),
(SUB64ri32_ND GR64:$src1, 0xffffffff80000000)>;
}
-def : Pat<(store (add (loadi64 addr:$dst), 0x0000000080000000), addr:$dst),
+def : Pat<(store (add_without_nuw (loadi64 addr:$dst), 0x0000000080000000), addr:$dst),
(SUB64mi32 addr:$dst, 0xffffffff80000000)>;
let Predicates = [HasNDD] in {
- def : Pat<(add(loadi64 ndd_addr:$src), 0x0000000080000000),
+ def : Pat<(add_without_nuw (loadi64 ndd_addr:$src), 0x0000000080000000),
(SUB64mi32_ND ndd_addr:$src, 0xffffffff80000000)>;
}
@@ -1691,7 +1698,7 @@ def : Pat<(or (and GR64:$dst, -65536),
def : Pat<(or (and GR32:$dst, -65536),
(i32 (zextloadi16 addr:$src))),
(INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm i16mem:$src), sub_16bit)>;
-
+
// To avoid needing to materialize an immediate in a register, use a 32-bit and
// with implicit zero-extension instead of a 64-bit and if the immediate has at
// least 32 bits of leading zeros. If in addition the last 32 bits can be
>From 2aec24c1e421c86813c19d200f82b3f4fd58a156 Mon Sep 17 00:00:00 2001
From: hesam-oxe <chngyzkhanwhsht at gmail.com>
Date: Sun, 13 Sep 2026 16:34:26 +0000
Subject: [PATCH 2/2] [X86] Update avx512vnni-combine.ll expectation for
add_without_nuw
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The 'add %r8, 128' in this loop carries NUW (produced by the vnni
combining), so with the add_without_nuw PatFrag it no longer converts
to 'subq $-128' — the ADD form is the expected output now. Also drop
a stray whitespace-only line introduced next to the INSERT_SUBREG
patterns.
---
llvm/lib/Target/X86/X86InstrCompiler.td | 2 +-
llvm/test/CodeGen/X86/avx512vnni-combine.ll | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 41fbdfde20874..04a15fd34ccbb 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -1698,7 +1698,7 @@ def : Pat<(or (and GR64:$dst, -65536),
def : Pat<(or (and GR32:$dst, -65536),
(i32 (zextloadi16 addr:$src))),
(INSERT_SUBREG (i32 (COPY $dst)), (MOV16rm i16mem:$src), sub_16bit)>;
-
+
// To avoid needing to materialize an immediate in a register, use a 32-bit and
// with implicit zero-extension instead of a 64-bit and if the immediate has at
// least 32 bits of leading zeros. If in addition the last 32 bits can be
diff --git a/llvm/test/CodeGen/X86/avx512vnni-combine.ll b/llvm/test/CodeGen/X86/avx512vnni-combine.ll
index b7d950e994241..a3345fb239b30 100644
--- a/llvm/test/CodeGen/X86/avx512vnni-combine.ll
+++ b/llvm/test/CodeGen/X86/avx512vnni-combine.ll
@@ -189,7 +189,7 @@ define void @bar_512(i32 %0, ptr %1, <8 x i64> %2, ptr %3) {
; CHECK-NEXT: vpaddd %zmm2, %zmm1, %zmm1
; CHECK-NEXT: vmovdqa64 %zmm1, (%rsi,%r8)
; CHECK-NEXT: addq $2, %rcx
-; CHECK-NEXT: subq $-128, %r8
+; CHECK-NEXT: addq $128, %r8
; CHECK-NEXT: cmpq %rcx, %rdi
; CHECK-NEXT: jne .LBB2_7
; CHECK-NEXT: .LBB2_3:
More information about the llvm-commits
mailing list