[llvm-branch-commits] [llvm] [InstCombine] Fold full-width `bitinsert` and `bitextract` to `bitcast` (PR #229147)
Pedro Lobo via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 6 05:03:58 PDT 2026
https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/229147
>From 471683748a8adb54af2d9eae778859512b294848 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 14:42:34 +0100
Subject: [PATCH 1/2] [InstCombine] Pre-commit tests for full-width
`bitinsert`/`bitextract`
---
.../InstCombine/bitinsert-bitextract.ll | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
diff --git a/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll b/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
new file mode 100644
index 0000000000000..8b2d825fa28c8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
@@ -0,0 +1,152 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+target datalayout = "p1:32:32"
+
+define i64 @bitextract_i64(b64 %x) {
+; CHECK-LABEL: define i64 @bitextract_i64(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i64, b64 [[X]], i32 0
+; CHECK-NEXT: ret i64 [[R]]
+;
+ %r = bitextract i64, b64 %x, i32 0
+ ret i64 %r
+}
+
+define double @bitextract_double(b64 %x) {
+; CHECK-LABEL: define double @bitextract_double(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract double, b64 [[X]], i32 0
+; CHECK-NEXT: ret double [[R]]
+;
+ %r = bitextract double, b64 %x, i32 0
+ ret double %r
+}
+
+define ptr @bitextract_ptr(b64 %x) {
+; CHECK-LABEL: define ptr @bitextract_ptr(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 [[X]], i32 0
+; CHECK-NEXT: ret ptr [[R]]
+;
+ %r = bitextract ptr, b64 %x, i32 0
+ ret ptr %r
+}
+
+define ptr addrspace(1) @bitextract_ptr_as1(b32 %x) {
+; CHECK-LABEL: define ptr addrspace(1) @bitextract_ptr_as1(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr addrspace(1), b32 [[X]], i32 0
+; CHECK-NEXT: ret ptr addrspace(1) [[R]]
+;
+ %r = bitextract ptr addrspace(1), b32 %x, i32 0
+ ret ptr addrspace(1) %r
+}
+
+define b64 @bitinsert_i64(b64 %x, i64 %y) {
+; CHECK-LABEL: define b64 @bitinsert_i64(
+; CHECK-SAME: b64 [[X:%.*]], i64 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], i64 [[Y]], i32 0
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %r = bitinsert b64 %x, i64 %y, i32 0
+ ret b64 %r
+}
+
+define b32 @bitinsert_float(b32 %x, float %y) {
+; CHECK-LABEL: define b32 @bitinsert_float(
+; CHECK-SAME: b32 [[X:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], float [[Y]], i32 0
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, float %y, i32 0
+ ret b32 %r
+}
+
+define b64 @bitinsert_ptr(b64 %x, ptr %y) {
+; CHECK-LABEL: define b64 @bitinsert_ptr(
+; CHECK-SAME: b64 [[X:%.*]], ptr [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], ptr [[Y]], i32 0
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %r = bitinsert b64 %x, ptr %y, i32 0
+ ret b64 %r
+}
+
+define i8 @bitextract_bitinsert(b32 %x, i8 %y) {
+; CHECK-LABEL: define i8 @bitextract_bitinsert(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[I]], i32 8
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %i = bitinsert b32 %x, i8 %y, i32 8
+ %r = bitextract i8, b32 %i, i32 8
+ ret i8 %r
+}
+
+; The result is narrower than the source.
+define i32 @neg_bitextract_partial(b64 %x) {
+; CHECK-LABEL: define i32 @neg_bitextract_partial(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i32, b64 [[X]], i32 0
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %r = bitextract i32, b64 %x, i32 0
+ ret i32 %r
+}
+
+; The pointer is narrower than the source.
+define ptr @neg_bitextract_ptr_narrow(b128 %x) {
+; CHECK-LABEL: define ptr @neg_bitextract_ptr_narrow(
+; CHECK-SAME: b128 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b128 [[X]], i32 0
+; CHECK-NEXT: ret ptr [[R]]
+;
+ %r = bitextract ptr, b128 %x, i32 0
+ ret ptr %r
+}
+
+; Pointers in addrspace(1) are 32 bits wide.
+define ptr addrspace(1) @neg_bitextract_ptr_as1(b64 %x) {
+; CHECK-LABEL: define ptr addrspace(1) @neg_bitextract_ptr_as1(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr addrspace(1), b64 [[X]], i32 0
+; CHECK-NEXT: ret ptr addrspace(1) [[R]]
+;
+ %r = bitextract ptr addrspace(1), b64 %x, i32 0
+ ret ptr addrspace(1) %r
+}
+
+; The offset may not be zero.
+define i64 @neg_bitextract_variable_offset(b64 %x, i32 %n) {
+; CHECK-LABEL: define i64 @neg_bitextract_variable_offset(
+; CHECK-SAME: b64 [[X:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i64, b64 [[X]], i32 [[N]]
+; CHECK-NEXT: ret i64 [[R]]
+;
+ %r = bitextract i64, b64 %x, i32 %n
+ ret i64 %r
+}
+
+; The value is narrower than the base.
+define b64 @neg_bitinsert_partial(b64 %x, i32 %y) {
+; CHECK-LABEL: define b64 @neg_bitinsert_partial(
+; CHECK-SAME: b64 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], i32 [[Y]], i32 0
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %r = bitinsert b64 %x, i32 %y, i32 0
+ ret b64 %r
+}
+
+; The offset may not be zero.
+define b64 @neg_bitinsert_variable_offset(b64 %x, i64 %y, i32 %n) {
+; CHECK-LABEL: define b64 @neg_bitinsert_variable_offset(
+; CHECK-SAME: b64 [[X:%.*]], i64 [[Y:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], i64 [[Y]], i32 [[N]]
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %r = bitinsert b64 %x, i64 %y, i32 %n
+ ret b64 %r
+}
>From c0f5d8f3ec766d22f93b35d4046f55a2e3b957d2 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 14:57:32 +0100
Subject: [PATCH 2/2] [InstCombine] Fold full-width `bitinsert` and
`bitextract` to `bitcast`
---
.../InstCombine/InstCombineInternal.h | 2 ++
.../InstCombine/InstructionCombining.cpp | 31 +++++++++++++++++++
.../InstCombine/bitinsert-bitextract.ll | 18 +++++------
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 388530edc10da..19137decb2751 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -192,6 +192,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
Instruction *simplifyBinOpSplats(ShuffleVectorInst &SVI);
Instruction *visitShuffleVectorInst(ShuffleVectorInst &SVI);
Instruction *visitExtractValueInst(ExtractValueInst &EV);
+ Instruction *visitBitInsertInst(BitInsertInst &BI);
+ Instruction *visitBitExtractInst(BitExtractInst &BE);
Instruction *visitLandingPadInst(LandingPadInst &LI);
Instruction *visitVAEndInst(VAEndInst &I);
Value *pushFreezeToPreventPoisonFromPropagating(FreezeInst &FI);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 265310ea0ff41..c2cf6bb1ed544 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4834,6 +4834,37 @@ Instruction *InstCombinerImpl::visitExtractValueInst(ExtractValueInst &EV) {
return nullptr;
}
+Instruction *InstCombinerImpl::visitBitInsertInst(BitInsertInst &BI) {
+ Value *Base = BI.getOperand(0);
+ Value *Val = BI.getOperand(1);
+ Value *Offset = BI.getOperand(2);
+ if (Value *V =
+ simplifyBitInsertInst(Base, Val, Offset, SQ.getWithInstruction(&BI)))
+ return replaceInstUsesWith(BI, V);
+
+ // bitinsert (bN x), (ty y), 0 -> bitcast y to bN, if ty is N bits wide
+ if (match(Offset, m_Zero()) && DL.getTypeSizeInBits(Val->getType()) ==
+ DL.getTypeSizeInBits(Base->getType()))
+ return new BitCastInst(Val, Base->getType());
+
+ return nullptr;
+}
+
+Instruction *InstCombinerImpl::visitBitExtractInst(BitExtractInst &BE) {
+ Value *Src = BE.getOperand(0);
+ Value *Offset = BE.getOperand(1);
+ if (Value *V = simplifyBitExtractInst(BE.getType(), Src, Offset,
+ SQ.getWithInstruction(&BE)))
+ return replaceInstUsesWith(BE, V);
+
+ // bitextract ty, (bN x), 0 -> bitcast x to ty, if ty is N bits wide
+ if (match(Offset, m_Zero()) && DL.getTypeSizeInBits(BE.getType()) ==
+ DL.getTypeSizeInBits(Src->getType()))
+ return new BitCastInst(Src, BE.getType());
+
+ return nullptr;
+}
+
/// Return 'true' if the given typeinfo will match anything.
static bool isCatchAll(EHPersonality Personality, Constant *TypeInfo) {
switch (Personality) {
diff --git a/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll b/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
index 8b2d825fa28c8..59aef89ea5d99 100644
--- a/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
+++ b/llvm/test/Transforms/InstCombine/bitinsert-bitextract.ll
@@ -6,7 +6,7 @@ target datalayout = "p1:32:32"
define i64 @bitextract_i64(b64 %x) {
; CHECK-LABEL: define i64 @bitextract_i64(
; CHECK-SAME: b64 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract i64, b64 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast b64 [[X]] to i64
; CHECK-NEXT: ret i64 [[R]]
;
%r = bitextract i64, b64 %x, i32 0
@@ -16,7 +16,7 @@ define i64 @bitextract_i64(b64 %x) {
define double @bitextract_double(b64 %x) {
; CHECK-LABEL: define double @bitextract_double(
; CHECK-SAME: b64 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract double, b64 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast b64 [[X]] to double
; CHECK-NEXT: ret double [[R]]
;
%r = bitextract double, b64 %x, i32 0
@@ -26,7 +26,7 @@ define double @bitextract_double(b64 %x) {
define ptr @bitextract_ptr(b64 %x) {
; CHECK-LABEL: define ptr @bitextract_ptr(
; CHECK-SAME: b64 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast b64 [[X]] to ptr
; CHECK-NEXT: ret ptr [[R]]
;
%r = bitextract ptr, b64 %x, i32 0
@@ -36,7 +36,7 @@ define ptr @bitextract_ptr(b64 %x) {
define ptr addrspace(1) @bitextract_ptr_as1(b32 %x) {
; CHECK-LABEL: define ptr addrspace(1) @bitextract_ptr_as1(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract ptr addrspace(1), b32 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast b32 [[X]] to ptr addrspace(1)
; CHECK-NEXT: ret ptr addrspace(1) [[R]]
;
%r = bitextract ptr addrspace(1), b32 %x, i32 0
@@ -46,7 +46,7 @@ define ptr addrspace(1) @bitextract_ptr_as1(b32 %x) {
define b64 @bitinsert_i64(b64 %x, i64 %y) {
; CHECK-LABEL: define b64 @bitinsert_i64(
; CHECK-SAME: b64 [[X:%.*]], i64 [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], i64 [[Y]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast i64 [[Y]] to b64
; CHECK-NEXT: ret b64 [[R]]
;
%r = bitinsert b64 %x, i64 %y, i32 0
@@ -56,7 +56,7 @@ define b64 @bitinsert_i64(b64 %x, i64 %y) {
define b32 @bitinsert_float(b32 %x, float %y) {
; CHECK-LABEL: define b32 @bitinsert_float(
; CHECK-SAME: b32 [[X:%.*]], float [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], float [[Y]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast float [[Y]] to b32
; CHECK-NEXT: ret b32 [[R]]
;
%r = bitinsert b32 %x, float %y, i32 0
@@ -66,7 +66,7 @@ define b32 @bitinsert_float(b32 %x, float %y) {
define b64 @bitinsert_ptr(b64 %x, ptr %y) {
; CHECK-LABEL: define b64 @bitinsert_ptr(
; CHECK-SAME: b64 [[X:%.*]], ptr [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], ptr [[Y]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitcast ptr [[Y]] to b64
; CHECK-NEXT: ret b64 [[R]]
;
%r = bitinsert b64 %x, ptr %y, i32 0
@@ -76,9 +76,7 @@ define b64 @bitinsert_ptr(b64 %x, ptr %y) {
define i8 @bitextract_bitinsert(b32 %x, i8 %y) {
; CHECK-LABEL: define i8 @bitextract_bitinsert(
; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 8
-; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[I]], i32 8
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[Y]]
;
%i = bitinsert b32 %x, i8 %y, i32 8
%r = bitextract i8, b32 %i, i32 8
More information about the llvm-branch-commits
mailing list