[llvm] [GIsel] Add constant-folding for more unary integer ops (PR #194265)
Alex MacLean via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 27 13:00:24 PDT 2026
https://github.com/AlexMaclean updated https://github.com/llvm/llvm-project/pull/194265
>From 65ccaaafd5f8576b80034c9622e300b296674eb1 Mon Sep 17 00:00:00 2001
From: Alex Maclean <amaclean at nvidia.com>
Date: Sun, 26 Apr 2026 13:28:45 -0700
Subject: [PATCH 1/2] [GIsel] Add constant-folding for more unary integer ops
---
.../llvm/CodeGen/GlobalISel/CombinerHelper.h | 8 +-
llvm/include/llvm/CodeGen/GlobalISel/Utils.h | 12 +-
.../include/llvm/Target/GlobalISel/Combine.td | 13 +-
llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp | 9 +-
.../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 8 +-
llvm/lib/CodeGen/GlobalISel/Utils.cpp | 18 +-
...ir => combine-constant-fold-unary-int.mir} | 173 ++++++++++++++++++
7 files changed, 213 insertions(+), 28 deletions(-)
rename llvm/test/CodeGen/AArch64/GlobalISel/{combine-constant-fold-count.mir => combine-constant-fold-unary-int.mir} (55%)
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
index b278a4eeda90f..97f29015c6911 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
@@ -409,9 +409,11 @@ class CombinerHelper {
void applyCombineConstantFoldFpUnary(MachineInstr &MI,
const ConstantFP *Cst) const;
- /// Constant fold G_CTLZ, G_CTTZ, G_CTPOP (and their _ZERO_UNDEF variants)
- /// when the operand is a scalar constant or a G_BUILD_VECTOR of constants.
- bool matchConstantFoldCountOp(MachineInstr &MI, BuildFnTy &MatchInfo) const;
+ /// Constant fold a unary integer op (G_CTLZ, G_CTTZ, G_CTPOP and their
+ /// _ZERO_UNDEF variants, G_ABS, G_BSWAP, G_BITREVERSE) when the operand is
+ /// a scalar constant or a G_BUILD_VECTOR of constants.
+ bool matchConstantFoldUnaryIntOp(MachineInstr &MI,
+ BuildFnTy &MatchInfo) const;
/// Transform IntToPtr(PtrToInt(x)) to x if cast is in the same address space.
bool matchCombineI2PToP2I(MachineInstr &MI, Register &Reg) const;
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
index 72e60ed8d5189..a861a60ecfcd3 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h
@@ -319,12 +319,12 @@ LLVM_ABI std::optional<APFloat>
ConstantFoldIntToFloat(unsigned Opcode, LLT DstTy, Register Src,
const MachineRegisterInfo &MRI);
-/// Tries to constant fold a bit-counting operation (G_CTLZ, G_CTTZ, G_CTPOP
-/// and their _ZERO_UNDEF variants) on \p Src. If \p Src is a vector then it
-/// tries to do an element-wise constant fold.
-LLVM_ABI SmallVector<APInt> ConstantFoldCountOp(unsigned Opcode, LLT DstTy,
- Register Src,
- const MachineRegisterInfo &MRI);
+/// Tries to constant fold a unary integer operation (G_CTLZ, G_CTTZ, G_CTPOP
+/// and their _ZERO_UNDEF variants, G_ABS, G_BSWAP, G_BITREVERSE) on \p Src.
+/// If \p Src is a vector then it tries to do an element-wise constant fold.
+LLVM_ABI SmallVector<APInt>
+ConstantFoldUnaryIntOp(unsigned Opcode, LLT DstTy, Register Src,
+ const MachineRegisterInfo &MRI);
LLVM_ABI std::optional<SmallVector<APInt>>
ConstantFoldICmp(unsigned Pred, const Register Op1, const Register Op2,
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 504ad0c9d817f..d4ef5722f44c5 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1390,16 +1390,17 @@ def constant_fold_cast_op : GICombineRule<
[{ return Helper.matchConstantFoldCastOp(*${d}, ${matchinfo}); }]),
(apply [{ Helper.replaceInstWithConstant(*${d}, ${matchinfo}); }])>;
-def count_op_frags : GICombinePatFrag<
+def unary_int_op_frags : GICombinePatFrag<
(outs root:$dst), (ins),
!foreach(op,
- [G_CTLZ, G_CTLZ_ZERO_UNDEF, G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP],
+ [G_CTLZ, G_CTLZ_ZERO_UNDEF, G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTPOP,
+ G_ABS, G_BSWAP, G_BITREVERSE],
(pattern (op $dst, $src)))>;
-def constant_fold_count_op : GICombineRule<
+def constant_fold_unary_int_op : GICombineRule<
(defs root:$dst, build_fn_matchinfo:$matchinfo),
- (match (count_op_frags $dst):$mi,
- [{ return Helper.matchConstantFoldCountOp(*${mi}, ${matchinfo}); }]),
+ (match (unary_int_op_frags $dst):$mi,
+ [{ return Helper.matchConstantFoldUnaryIntOp(*${mi}, ${matchinfo}); }]),
(apply [{ Helper.applyBuildFn(*${mi}, ${matchinfo}); }])>;
def mulo_by_2: GICombineRule<
@@ -2378,7 +2379,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine,
div_rem_to_divrem, funnel_shift_combines, bitreverse_shift, commute_shift,
form_bitfield_extract, constant_fold_binops, constant_fold_fma,
- constant_fold_cast_op, constant_fold_count_op, fabs_fneg_fold,
+ constant_fold_cast_op, constant_fold_unary_int_op, fabs_fneg_fold,
mulh_combines, redundant_neg_operands,
and_or_disjoint_mask, fma_combines, fold_binop_into_select,
intrem_combines, intdiv_combines, fdiv_repeated_divison,
diff --git a/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp
index 28bc880af962e..ff96d7a3cbd9a 100644
--- a/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp
@@ -285,11 +285,14 @@ MachineInstrBuilder CSEMIRBuilder::buildInstr(unsigned Opc,
case TargetOpcode::G_CTLZ_ZERO_UNDEF:
case TargetOpcode::G_CTTZ:
case TargetOpcode::G_CTTZ_ZERO_UNDEF:
- case TargetOpcode::G_CTPOP: {
+ case TargetOpcode::G_CTPOP:
+ case TargetOpcode::G_ABS:
+ case TargetOpcode::G_BSWAP:
+ case TargetOpcode::G_BITREVERSE: {
assert(SrcOps.size() == 1 && "Expected one source");
assert(DstOps.size() == 1 && "Expected one dest");
- auto Csts = ConstantFoldCountOp(Opc, DstOps[0].getLLTTy(*getMRI()),
- SrcOps[0].getReg(), *getMRI());
+ auto Csts = ConstantFoldUnaryIntOp(Opc, DstOps[0].getLLTTy(*getMRI()),
+ SrcOps[0].getReg(), *getMRI());
if (Csts.empty())
break;
if (Csts.size() == 1)
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 07696a0b6f3b3..95b7f864c16fd 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -5320,11 +5320,11 @@ bool CombinerHelper::matchConstantFoldCastOp(MachineInstr &MI,
return false;
}
-bool CombinerHelper::matchConstantFoldCountOp(MachineInstr &MI,
- BuildFnTy &MatchInfo) const {
+bool CombinerHelper::matchConstantFoldUnaryIntOp(MachineInstr &MI,
+ BuildFnTy &MatchInfo) const {
Register Dst = MI.getOperand(0).getReg();
- auto Csts = ConstantFoldCountOp(MI.getOpcode(), MRI.getType(Dst),
- MI.getOperand(1).getReg(), MRI);
+ auto Csts = ConstantFoldUnaryIntOp(MI.getOpcode(), MRI.getType(Dst),
+ MI.getOperand(1).getReg(), MRI);
if (Csts.empty())
return false;
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index 891d55a85db7d..b3aac99cfa427 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -940,11 +940,11 @@ llvm::ConstantFoldIntToFloat(unsigned Opcode, LLT DstTy, Register Src,
return std::nullopt;
}
-SmallVector<APInt> llvm::ConstantFoldCountOp(unsigned Opcode, LLT DstTy,
- Register Src,
- const MachineRegisterInfo &MRI) {
+SmallVector<APInt>
+llvm::ConstantFoldUnaryIntOp(unsigned Opcode, LLT DstTy, Register Src,
+ const MachineRegisterInfo &MRI) {
unsigned EltBits = DstTy.getScalarSizeInBits();
- auto Count = [Opcode, EltBits](const APInt &V) -> APInt {
+ auto Fold = [Opcode, EltBits](const APInt &V) -> APInt {
switch (Opcode) {
case TargetOpcode::G_CTLZ:
case TargetOpcode::G_CTLZ_ZERO_UNDEF:
@@ -954,13 +954,19 @@ SmallVector<APInt> llvm::ConstantFoldCountOp(unsigned Opcode, LLT DstTy,
return APInt(EltBits, V.countr_zero());
case TargetOpcode::G_CTPOP:
return APInt(EltBits, V.popcount());
+ case TargetOpcode::G_ABS:
+ return V.abs();
+ case TargetOpcode::G_BSWAP:
+ return V.byteSwap();
+ case TargetOpcode::G_BITREVERSE:
+ return V.reverseBits();
}
- llvm_unreachable("unexpected opcode in ConstantFoldCountOp");
+ llvm_unreachable("unexpected opcode in ConstantFoldUnaryIntOp");
};
auto tryFoldScalar = [&](Register R) -> std::optional<APInt> {
if (auto MaybeCst = getIConstantVRegVal(R, MRI))
- return Count(*MaybeCst);
+ return Fold(*MaybeCst);
return std::nullopt;
};
if (MRI.getType(Src).isVector()) {
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-count.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-unary-int.mir
similarity index 55%
rename from llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-count.mir
rename to llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-unary-int.mir
index 2a2a84c368784..cba19b49d7687 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-count.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-constant-fold-unary-int.mir
@@ -225,3 +225,176 @@ body: |
$w0 = COPY %res(s32)
RET_ReallyLR implicit $w0
...
+---
+name: abs_s32_pos
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: abs_s32_pos
+ ; CHECK: %a:_(s32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: $w0 = COPY %a(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 42
+ %res:_(s32) = G_ABS %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+name: abs_s32_neg
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: abs_s32_neg
+ ; CHECK: %res:_(s32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: $w0 = COPY %res(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 -42
+ %res:_(s32) = G_ABS %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+# abs of INT_MIN stays INT_MIN.
+name: abs_s32_int_min
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: abs_s32_int_min
+ ; CHECK: %a:_(s32) = G_CONSTANT i32 -2147483648
+ ; CHECK-NEXT: $w0 = COPY %a(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 -2147483648
+ %res:_(s32) = G_ABS %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+name: abs_v4s32
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: abs_v4s32
+ ; CHECK: %c1:_(s32) = G_CONSTANT i32 2
+ ; CHECK-NEXT: %c3:_(s32) = G_CONSTANT i32 4
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 3
+ ; CHECK-NEXT: %res:_(<4 x s32>) = G_BUILD_VECTOR [[C]](s32), %c1(s32), [[C1]](s32), %c3(s32)
+ ; CHECK-NEXT: $q0 = COPY %res(<4 x s32>)
+ ; CHECK-NEXT: RET_ReallyLR implicit $q0
+ %c0:_(s32) = G_CONSTANT i32 -1
+ %c1:_(s32) = G_CONSTANT i32 2
+ %c2:_(s32) = G_CONSTANT i32 -3
+ %c3:_(s32) = G_CONSTANT i32 4
+ %v:_(<4 x s32>) = G_BUILD_VECTOR %c0, %c1, %c2, %c3
+ %res:_(<4 x s32>) = G_ABS %v
+ $q0 = COPY %res(<4 x s32>)
+ RET_ReallyLR implicit $q0
+...
+---
+name: bswap_s32
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bswap_s32
+ ; CHECK: %res:_(s32) = G_CONSTANT i32 2018915346
+ ; CHECK-NEXT: $w0 = COPY %res(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 305419896
+ %res:_(s32) = G_BSWAP %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+name: bswap_s64
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bswap_s64
+ ; CHECK: %a:_(s64) = G_CONSTANT i64 -1
+ ; CHECK-NEXT: $x0 = COPY %a(s64)
+ ; CHECK-NEXT: RET_ReallyLR implicit $x0
+ %a:_(s64) = G_CONSTANT i64 -1
+ %res:_(s64) = G_BSWAP %a
+ $x0 = COPY %res(s64)
+ RET_ReallyLR implicit $x0
+...
+---
+name: bswap_v4s32
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bswap_v4s32
+ ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16777216
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 33554432
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 67108864
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 134217728
+ ; CHECK-NEXT: %res:_(<4 x s32>) = G_BUILD_VECTOR [[C]](s32), [[C1]](s32), [[C2]](s32), [[C3]](s32)
+ ; CHECK-NEXT: $q0 = COPY %res(<4 x s32>)
+ ; CHECK-NEXT: RET_ReallyLR implicit $q0
+ %c0:_(s32) = G_CONSTANT i32 1
+ %c1:_(s32) = G_CONSTANT i32 2
+ %c2:_(s32) = G_CONSTANT i32 4
+ %c3:_(s32) = G_CONSTANT i32 8
+ %v:_(<4 x s32>) = G_BUILD_VECTOR %c0, %c1, %c2, %c3
+ %res:_(<4 x s32>) = G_BSWAP %v
+ $q0 = COPY %res(<4 x s32>)
+ RET_ReallyLR implicit $q0
+...
+---
+name: bitreverse_s32
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bitreverse_s32
+ ; CHECK: %res:_(s32) = G_CONSTANT i32 -2147483648
+ ; CHECK-NEXT: $w0 = COPY %res(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 1
+ %res:_(s32) = G_BITREVERSE %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+name: bitreverse_s32_all_ones
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bitreverse_s32_all_ones
+ ; CHECK: %a:_(s32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: $w0 = COPY %a(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = G_CONSTANT i32 -1
+ %res:_(s32) = G_BITREVERSE %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
+---
+name: bitreverse_v4s32
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: bitreverse_v4s32
+ ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 -2147483648
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1073741824
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 536870912
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 268435456
+ ; CHECK-NEXT: %res:_(<4 x s32>) = G_BUILD_VECTOR [[C]](s32), [[C1]](s32), [[C2]](s32), [[C3]](s32)
+ ; CHECK-NEXT: $q0 = COPY %res(<4 x s32>)
+ ; CHECK-NEXT: RET_ReallyLR implicit $q0
+ %c0:_(s32) = G_CONSTANT i32 1
+ %c1:_(s32) = G_CONSTANT i32 2
+ %c2:_(s32) = G_CONSTANT i32 4
+ %c3:_(s32) = G_CONSTANT i32 8
+ %v:_(<4 x s32>) = G_BUILD_VECTOR %c0, %c1, %c2, %c3
+ %res:_(<4 x s32>) = G_BITREVERSE %v
+ $q0 = COPY %res(<4 x s32>)
+ RET_ReallyLR implicit $q0
+...
+---
+# Negative test: non-constant source should not be folded.
+name: abs_nonconstant
+body: |
+ bb.0:
+ liveins: $w0
+ ; CHECK-LABEL: name: abs_nonconstant
+ ; CHECK: liveins: $w0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %a:_(s32) = COPY $w0
+ ; CHECK-NEXT: %res:_(s32) = G_ABS %a
+ ; CHECK-NEXT: $w0 = COPY %res(s32)
+ ; CHECK-NEXT: RET_ReallyLR implicit $w0
+ %a:_(s32) = COPY $w0
+ %res:_(s32) = G_ABS %a
+ $w0 = COPY %res(s32)
+ RET_ReallyLR implicit $w0
+...
>From 834a8167e5f76fc16e6904900569a30881c7be93 Mon Sep 17 00:00:00 2001
From: Alex Maclean <amaclean at nvidia.com>
Date: Mon, 27 Apr 2026 13:00:09 -0700
Subject: [PATCH 2/2] fixup
---
.../CodeGen/SPIRV/extensions/enable-all-extensions-but-one.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/extensions/enable-all-extensions-but-one.ll b/llvm/test/CodeGen/SPIRV/extensions/enable-all-extensions-but-one.ll
index aeef8392468b6..b1381679470e1 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/enable-all-extensions-but-one.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/enable-all-extensions-but-one.ll
@@ -6,8 +6,8 @@
@G = global i32 0
-define i6 @foo() {
- %call = tail call i32 @llvm.bitreverse.i32(i32 42)
+define i6 @foo(i32 %x) {
+ %call = tail call i32 @llvm.bitreverse.i32(i32 %x)
store i32 %call, ptr @G
ret i6 2
}
More information about the llvm-commits
mailing list