[llvm-branch-commits] [llvm] [InstSimplify] Simplify `bitinsert` and `bitextract` (PR #229125)
Pedro Lobo via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 6 05:04:02 PDT 2026
https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/229125
>From 05c6481ca19ffb5f464cd60fe5b23f0d88a3f3c4 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 14:30:15 +0100
Subject: [PATCH 1/3] [InstSimplify] Pre-commit tests for `bitinsert` and
`bitextract`
---
.../InstSimplify/bitinsert-bitextract.ll | 331 ++++++++++++++++++
1 file changed, 331 insertions(+)
create mode 100644 llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
diff --git a/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
new file mode 100644
index 0000000000000..45c141da7dd24
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
@@ -0,0 +1,331 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
+
+define i8 @bitextract_out_of_range(b32 %x) {
+; CHECK-LABEL: define i8 @bitextract_out_of_range(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 25
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %r = bitextract i8, b32 %x, i32 25
+ ret i8 %r
+}
+
+; The last bit is in range.
+define i8 @neg_bitextract_in_range(b32 %x) {
+; CHECK-LABEL: define i8 @neg_bitextract_in_range(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 24
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %r = bitextract i8, b32 %x, i32 24
+ ret i8 %r
+}
+
+define ptr @bitextract_ptr_out_of_range(b64 %x) {
+; CHECK-LABEL: define ptr @bitextract_ptr_out_of_range(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 [[X]], i32 8
+; CHECK-NEXT: ret ptr [[R]]
+;
+ %r = bitextract ptr, b64 %x, i32 8
+ ret ptr %r
+}
+
+define ptr @bitextract_ptr_constant_out_of_range() {
+; CHECK-LABEL: define ptr @bitextract_ptr_constant_out_of_range() {
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 1234605616436508552, i32 8
+; CHECK-NEXT: ret ptr [[R]]
+;
+ %r = bitextract ptr, b64 1234605616436508552, i32 8
+ ret ptr %r
+}
+
+define b32 @bitinsert_out_of_range(b32 %x, i8 %y) {
+; CHECK-LABEL: define b32 @bitinsert_out_of_range(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 25
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, i8 %y, i32 25
+ ret b32 %r
+}
+
+; The last bit is in range.
+define b32 @neg_bitinsert_in_range(b32 %x, i8 %y) {
+; CHECK-LABEL: define b32 @neg_bitinsert_in_range(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 24
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, i8 %y, i32 24
+ ret b32 %r
+}
+
+define b64 @bitinsert_ptr_out_of_range(b64 %x, ptr %p) {
+; CHECK-LABEL: define b64 @bitinsert_ptr_out_of_range(
+; CHECK-SAME: b64 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], ptr [[P]], i32 8
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %r = bitinsert b64 %x, ptr %p, i32 8
+ ret b64 %r
+}
+
+define i8 @bitextract_poison_offset(b32 %x) {
+; CHECK-LABEL: define i8 @bitextract_poison_offset(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 poison
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %r = bitextract i8, b32 %x, i32 poison
+ ret i8 %r
+}
+
+define i8 @bitextract_undef_offset(b32 %x) {
+; CHECK-LABEL: define i8 @bitextract_undef_offset(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 undef
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %r = bitextract i8, b32 %x, i32 undef
+ ret i8 %r
+}
+
+define b32 @bitinsert_poison_offset(b32 %x, i8 %y) {
+; CHECK-LABEL: define b32 @bitinsert_poison_offset(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 poison
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, i8 %y, i32 poison
+ ret b32 %r
+}
+
+define b32 @bitinsert_undef_offset(b32 %x, i8 %y) {
+; CHECK-LABEL: define b32 @bitinsert_undef_offset(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 undef
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, i8 %y, i32 undef
+ ret b32 %r
+}
+
+define b32 @bitextract_full_width(b32 %x) {
+; CHECK-LABEL: define b32 @bitextract_full_width(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract b32, b32 [[X]], i32 0
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitextract b32, b32 %x, i32 0
+ ret b32 %r
+}
+
+; InstSimplify can't create a bitcast.
+define i32 @neg_bitextract_full_width_i32(b32 %x) {
+; CHECK-LABEL: define i32 @neg_bitextract_full_width_i32(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitextract i32, b32 [[X]], i32 0
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %r = bitextract i32, b32 %x, i32 0
+ ret i32 %r
+}
+
+define b32 @bitinsert_full_width(b32 %x, b32 %y) {
+; CHECK-LABEL: define b32 @bitinsert_full_width(
+; CHECK-SAME: b32 [[X:%.*]], b32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b32 [[Y]], i32 0
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, b32 %y, i32 0
+ ret b32 %r
+}
+
+; InstSimplify can't create a bitcast.
+define b32 @neg_bitinsert_full_width_i32(b32 %x, i32 %y) {
+; CHECK-LABEL: define b32 @neg_bitinsert_full_width_i32(
+; CHECK-SAME: b32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i32 [[Y]], i32 0
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %r = bitinsert b32 %x, i32 %y, i32 0
+ ret b32 %r
+}
+
+define i8 @bitextract_bitinsert_i8(b32 %x, i8 %y) {
+; CHECK-LABEL: define i8 @bitextract_bitinsert_i8(
+; 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
+}
+
+define b8 @bitextract_bitinsert_b8(b32 %x, b8 %y) {
+; CHECK-LABEL: define b8 @bitextract_bitinsert_b8(
+; CHECK-SAME: b32 [[X:%.*]], b8 [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], b8 [[Y]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitextract b8, b32 [[I]], i32 8
+; CHECK-NEXT: ret b8 [[R]]
+;
+ %i = bitinsert b32 %x, b8 %y, i32 8
+ %r = bitextract b8, b32 %i, i32 8
+ ret b8 %r
+}
+
+define float @bitextract_bitinsert_float(b64 %x, float %y) {
+; CHECK-LABEL: define float @bitextract_bitinsert_float(
+; CHECK-SAME: b64 [[X:%.*]], float [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b64 [[X]], float [[Y]], i32 32
+; CHECK-NEXT: [[R:%.*]] = bitextract float, b64 [[I]], i32 32
+; CHECK-NEXT: ret float [[R]]
+;
+ %i = bitinsert b64 %x, float %y, i32 32
+ %r = bitextract float, b64 %i, i32 32
+ ret float %r
+}
+
+define ptr @bitextract_bitinsert_ptr(b128 %x, ptr %y) {
+; CHECK-LABEL: define ptr @bitextract_bitinsert_ptr(
+; CHECK-SAME: b128 [[X:%.*]], ptr [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b128 [[X]], ptr [[Y]], i32 64
+; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b128 [[I]], i32 64
+; CHECK-NEXT: ret ptr [[R]]
+;
+ %i = bitinsert b128 %x, ptr %y, i32 64
+ %r = bitextract ptr, b128 %i, i32 64
+ ret ptr %r
+}
+
+define i8 @bitextract_bitinsert_variable_offset(b32 %x, i8 %y, i32 %n) {
+; CHECK-LABEL: define i8 @bitextract_bitinsert_variable_offset(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 [[N]]
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[I]], i32 [[N]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %i = bitinsert b32 %x, i8 %y, i32 %n
+ %r = bitextract i8, b32 %i, i32 %n
+ ret i8 %r
+}
+
+; The offsets differ.
+define i8 @neg_bitextract_bitinsert_other_offset(b32 %x, i8 %y) {
+; CHECK-LABEL: define i8 @neg_bitextract_bitinsert_other_offset(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[I]], i32 16
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %i = bitinsert b32 %x, i8 %y, i32 8
+ %r = bitextract i8, b32 %i, i32 16
+ ret i8 %r
+}
+
+; The types differ.
+define b8 @neg_bitextract_bitinsert_other_type(b32 %x, i8 %y) {
+; CHECK-LABEL: define b8 @neg_bitextract_bitinsert_other_type(
+; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitextract b8, b32 [[I]], i32 8
+; CHECK-NEXT: ret b8 [[R]]
+;
+ %i = bitinsert b32 %x, i8 %y, i32 8
+ %r = bitextract b8, b32 %i, i32 8
+ ret b8 %r
+}
+
+define b32 @bitinsert_bitextract_b8(b32 %x) {
+; CHECK-LABEL: define b32 @bitinsert_bitextract_b8(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[X]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 8
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %e = bitextract b8, b32 %x, i32 8
+ %r = bitinsert b32 %x, b8 %e, i32 8
+ ret b32 %r
+}
+
+define b32 @bitinsert_bitextract_variable_offset(b32 %x, i32 %n) {
+; CHECK-LABEL: define b32 @bitinsert_bitextract_variable_offset(
+; CHECK-SAME: b32 [[X:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[X]], i32 [[N]]
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 [[N]]
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %e = bitextract b8, b32 %x, i32 %n
+ %r = bitinsert b32 %x, b8 %e, i32 %n
+ ret b32 %r
+}
+
+; An integer extract strips provenance.
+define b32 @neg_bitinsert_bitextract_i8(b32 %x) {
+; CHECK-LABEL: define b32 @neg_bitinsert_bitextract_i8(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract i8, b32 [[X]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[E]], i32 8
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %e = bitextract i8, b32 %x, i32 8
+ %r = bitinsert b32 %x, i8 %e, i32 8
+ ret b32 %r
+}
+
+; A floating-point extract strips provenance.
+define b64 @neg_bitinsert_bitextract_double(b64 %x) {
+; CHECK-LABEL: define b64 @neg_bitinsert_bitextract_double(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract double, b64 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], double [[E]], i32 0
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %e = bitextract double, b64 %x, i32 0
+ %r = bitinsert b64 %x, double %e, i32 0
+ ret b64 %r
+}
+
+; A pointer extract returns a new pointer without provenance, unless the bits
+; are one pointer in order.
+define b64 @neg_bitinsert_bitextract_ptr(b64 %x) {
+; CHECK-LABEL: define b64 @neg_bitinsert_bitextract_ptr(
+; CHECK-SAME: b64 [[X:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract ptr, b64 [[X]], i32 0
+; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], ptr [[E]], i32 0
+; CHECK-NEXT: ret b64 [[R]]
+;
+ %e = bitextract ptr, b64 %x, i32 0
+ %r = bitinsert b64 %x, ptr %e, i32 0
+ ret b64 %r
+}
+
+; The bases differ.
+define b32 @neg_bitinsert_bitextract_other_base(b32 %x, b32 %y) {
+; CHECK-LABEL: define b32 @neg_bitinsert_bitextract_other_base(
+; CHECK-SAME: b32 [[X:%.*]], b32 [[Y:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[Y]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 8
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %e = bitextract b8, b32 %y, i32 8
+ %r = bitinsert b32 %x, b8 %e, i32 8
+ ret b32 %r
+}
+
+; The offsets differ.
+define b32 @neg_bitinsert_bitextract_other_offset(b32 %x) {
+; CHECK-LABEL: define b32 @neg_bitinsert_bitextract_other_offset(
+; CHECK-SAME: b32 [[X:%.*]]) {
+; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[X]], i32 8
+; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 16
+; CHECK-NEXT: ret b32 [[R]]
+;
+ %e = bitextract b8, b32 %x, i32 8
+ %r = bitinsert b32 %x, b8 %e, i32 16
+ ret b32 %r
+}
>From f2511436bebf26abad0f1bf50c963cf337f14586 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 14:30:16 +0100
Subject: [PATCH 2/3] [InstSimplify] Simplify `bitinsert` and `bitextract`
---
.../llvm/Analysis/InstSimplifyFolder.h | 6 +-
.../llvm/Analysis/InstructionSimplify.h | 8 ++
llvm/include/llvm/IR/PatternMatch.h | 15 ++++
llvm/lib/Analysis/InstructionSimplify.cpp | 89 +++++++++++++++++++
.../InstSimplify/bitinsert-bitextract.ll | 61 ++++---------
llvm/unittests/IR/PatternMatch.cpp | 29 ++++++
6 files changed, 161 insertions(+), 47 deletions(-)
diff --git a/llvm/include/llvm/Analysis/InstSimplifyFolder.h b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
index 7171ef26f6953..eda49b2413eea 100644
--- a/llvm/include/llvm/Analysis/InstSimplifyFolder.h
+++ b/llvm/include/llvm/Analysis/InstSimplifyFolder.h
@@ -116,13 +116,11 @@ class LLVM_ABI InstSimplifyFolder final : public IRBuilderFolder {
}
Value *FoldBitInsert(Value *Base, Value *Val, Value *Offset) const override {
- // TODO
- return nullptr;
+ return simplifyBitInsertInst(Base, Val, Offset, SQ);
}
Value *FoldBitExtract(Type *Ty, Value *Src, Value *Offset) const override {
- // TODO
- return nullptr;
+ return simplifyBitExtractInst(Ty, Src, Offset, SQ);
}
Value *FoldCast(Instruction::CastOps Op, Value *V,
diff --git a/llvm/include/llvm/Analysis/InstructionSimplify.h b/llvm/include/llvm/Analysis/InstructionSimplify.h
index 3568422c8348b..48f2f7e0a9a55 100644
--- a/llvm/include/llvm/Analysis/InstructionSimplify.h
+++ b/llvm/include/llvm/Analysis/InstructionSimplify.h
@@ -190,6 +190,14 @@ LLVM_ABI Value *simplifyExtractValueInst(Value *Agg, ArrayRef<unsigned> Idxs,
LLVM_ABI Value *simplifyExtractElementInst(Value *Vec, Value *Idx,
const SimplifyQuery &Q);
+/// Given operands for a BitInsertInst, fold the result or return null.
+LLVM_ABI Value *simplifyBitInsertInst(Value *Base, Value *Val, Value *Offset,
+ const SimplifyQuery &Q);
+
+/// Given operands for a BitExtractInst, fold the result or return null.
+LLVM_ABI Value *simplifyBitExtractInst(Type *Ty, Value *Src, Value *Offset,
+ const SimplifyQuery &Q);
+
/// Given operands for a CastInst, fold the result or return null.
LLVM_ABI Value *simplifyCastInst(unsigned CastOpc, Value *Op, Type *Ty,
const SimplifyQuery &Q);
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 4b39b940338a9..33d94f1607b7a 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -2002,6 +2002,21 @@ m_ExtractElt(const Val_t &Val, const Idx_t &Idx) {
return TwoOps_match<Val_t, Idx_t, Instruction::ExtractElement>(Val, Idx);
}
+/// Matches BitInsertInst.
+template <typename Base_t, typename Val_t, typename Off_t>
+inline ThreeOps_match<Base_t, Val_t, Off_t, Instruction::BitInsert>
+m_BitInsert(const Base_t &Base, const Val_t &Val, const Off_t &Off) {
+ return ThreeOps_match<Base_t, Val_t, Off_t, Instruction::BitInsert>(Base, Val,
+ Off);
+}
+
+/// Matches BitExtractInst.
+template <typename Src_t, typename Off_t>
+inline TwoOps_match<Src_t, Off_t, Instruction::BitExtract>
+m_BitExtract(const Src_t &Src, const Off_t &Off) {
+ return TwoOps_match<Src_t, Off_t, Instruction::BitExtract>(Src, Off);
+}
+
/// Matches shuffle.
template <typename T0, typename T1, typename T2> struct Shuffle_match {
T0 Op1;
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 74a266660b234..3923cfbdff7b6 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -5616,6 +5616,89 @@ Value *llvm::simplifyExtractElementInst(Value *Vec, Value *Idx,
return ::simplifyExtractElementInst(Vec, Idx, Q, RecursionLimit);
}
+/// Return true if accessing the bit range of type Ty at Offset in a value of
+/// type SrcTy is poison.
+static bool isPoisonBitRange(Value *Offset, Type *Ty, Type *SrcTy,
+ const SimplifyQuery &Q) {
+ // A poison offset makes the result poison. An undef offset can be chosen to
+ // be out of range, which also makes the result poison.
+ if (isa<PoisonValue>(Offset) || Q.isUndefValue(Offset))
+ return true;
+
+ // Accessing bits past the end of the value is poison.
+ const APInt *Off;
+ return match(Offset, m_APInt(Off)) &&
+ Off->getZExtValue() + Q.DL.getTypeSizeInBits(Ty).getFixedValue() >
+ Q.DL.getTypeSizeInBits(SrcTy).getFixedValue();
+}
+
+/// Given operands for a BitInsertInst, see if we can fold the result.
+/// If not, this returns null.
+static Value *simplifyBitInsertInst(Value *Base, Value *Val, Value *Offset,
+ const SimplifyQuery &Q, unsigned) {
+ auto *CBase = dyn_cast<Constant>(Base);
+ auto *CVal = dyn_cast<Constant>(Val);
+ auto *COffset = dyn_cast<Constant>(Offset);
+ if (CBase && CVal && COffset)
+ if (Constant *C = ConstantFoldBitInsertInstruction(CBase, CVal, COffset))
+ return C;
+
+ // bitinsert x, y, poison/undef/out_of_range -> poison
+ if (isPoisonBitRange(Offset, Val->getType(), Base->getType(), Q))
+ return PoisonValue::get(Base->getType());
+
+ // bitinsert x, (bN y), n -> y, since any n != 0 is poison
+ if (Val->getType() == Base->getType())
+ return Val;
+
+ // bitinsert x, (bitextract bM, x, n), n -> x
+ // Only fold byte extracts, which keep every bit as is. Other extracts can
+ // drop provenance, or make the whole value poison if any bit is poison, so
+ // reinserting them doesn't always give back the original value.
+ if (Val->getType()->isByteTy() &&
+ match(Val, m_BitExtract(m_Specific(Base), m_Specific(Offset))))
+ return Base;
+
+ return nullptr;
+}
+
+Value *llvm::simplifyBitInsertInst(Value *Base, Value *Val, Value *Offset,
+ const SimplifyQuery &Q) {
+ return ::simplifyBitInsertInst(Base, Val, Offset, Q, RecursionLimit);
+}
+
+/// Given operands for a BitExtractInst, see if we can fold the result.
+/// If not, this returns null.
+static Value *simplifyBitExtractInst(Type *Ty, Value *Src, Value *Offset,
+ const SimplifyQuery &Q, unsigned) {
+ auto *CSrc = dyn_cast<Constant>(Src);
+ auto *COffset = dyn_cast<Constant>(Offset);
+ if (CSrc && COffset)
+ if (Constant *C = ConstantFoldBitExtractInstruction(Ty, CSrc, COffset))
+ return C;
+
+ // bitextract ty, x, poison/undef/out_of_range -> poison
+ if (isPoisonBitRange(Offset, Ty, Src->getType(), Q))
+ return PoisonValue::get(Ty);
+
+ // bitextract bN, (bN x), n -> x, since any n != 0 is poison
+ if (Src->getType() == Ty)
+ return Src;
+
+ // bitextract ty, (bitinsert x, (ty y), n), n -> y
+ Value *Val;
+ if (match(Src, m_BitInsert(m_Value(), m_Value(Val), m_Specific(Offset))) &&
+ Val->getType() == Ty)
+ return Val;
+
+ return nullptr;
+}
+
+Value *llvm::simplifyBitExtractInst(Type *Ty, Value *Src, Value *Offset,
+ const SimplifyQuery &Q) {
+ return ::simplifyBitExtractInst(Ty, Src, Offset, Q, RecursionLimit);
+}
+
/// See if we can fold the given phi. If not, returns null.
static Value *simplifyPHINode(PHINode *PN, ArrayRef<Value *> IncomingValues,
const SimplifyQuery &Q) {
@@ -7866,6 +7949,12 @@ static Value *simplifyInstructionWithOperands(Instruction *I,
}
case Instruction::ExtractElement:
return simplifyExtractElementInst(NewOps[0], NewOps[1], Q, MaxRecurse);
+ case Instruction::BitInsert:
+ return simplifyBitInsertInst(NewOps[0], NewOps[1], NewOps[2], Q,
+ MaxRecurse);
+ case Instruction::BitExtract:
+ return simplifyBitExtractInst(I->getType(), NewOps[0], NewOps[1], Q,
+ MaxRecurse);
case Instruction::ShuffleVector: {
auto *SVI = cast<ShuffleVectorInst>(I);
return simplifyShuffleVectorInst(NewOps[0], NewOps[1],
diff --git a/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
index 45c141da7dd24..91d721b006f2d 100644
--- a/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
+++ b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll
@@ -4,8 +4,7 @@
define i8 @bitextract_out_of_range(b32 %x) {
; CHECK-LABEL: define i8 @bitextract_out_of_range(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 25
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 poison
;
%r = bitextract i8, b32 %x, i32 25
ret i8 %r
@@ -25,8 +24,7 @@ define i8 @neg_bitextract_in_range(b32 %x) {
define ptr @bitextract_ptr_out_of_range(b64 %x) {
; CHECK-LABEL: define ptr @bitextract_ptr_out_of_range(
; CHECK-SAME: b64 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 [[X]], i32 8
-; CHECK-NEXT: ret ptr [[R]]
+; CHECK-NEXT: ret ptr poison
;
%r = bitextract ptr, b64 %x, i32 8
ret ptr %r
@@ -34,8 +32,7 @@ define ptr @bitextract_ptr_out_of_range(b64 %x) {
define ptr @bitextract_ptr_constant_out_of_range() {
; CHECK-LABEL: define ptr @bitextract_ptr_constant_out_of_range() {
-; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b64 1234605616436508552, i32 8
-; CHECK-NEXT: ret ptr [[R]]
+; CHECK-NEXT: ret ptr poison
;
%r = bitextract ptr, b64 1234605616436508552, i32 8
ret ptr %r
@@ -44,8 +41,7 @@ define ptr @bitextract_ptr_constant_out_of_range() {
define b32 @bitinsert_out_of_range(b32 %x, i8 %y) {
; CHECK-LABEL: define b32 @bitinsert_out_of_range(
; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 25
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 poison
;
%r = bitinsert b32 %x, i8 %y, i32 25
ret b32 %r
@@ -65,8 +61,7 @@ define b32 @neg_bitinsert_in_range(b32 %x, i8 %y) {
define b64 @bitinsert_ptr_out_of_range(b64 %x, ptr %p) {
; CHECK-LABEL: define b64 @bitinsert_ptr_out_of_range(
; CHECK-SAME: b64 [[X:%.*]], ptr [[P:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b64 [[X]], ptr [[P]], i32 8
-; CHECK-NEXT: ret b64 [[R]]
+; CHECK-NEXT: ret b64 poison
;
%r = bitinsert b64 %x, ptr %p, i32 8
ret b64 %r
@@ -75,8 +70,7 @@ define b64 @bitinsert_ptr_out_of_range(b64 %x, ptr %p) {
define i8 @bitextract_poison_offset(b32 %x) {
; CHECK-LABEL: define i8 @bitextract_poison_offset(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 poison
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 poison
;
%r = bitextract i8, b32 %x, i32 poison
ret i8 %r
@@ -85,8 +79,7 @@ define i8 @bitextract_poison_offset(b32 %x) {
define i8 @bitextract_undef_offset(b32 %x) {
; CHECK-LABEL: define i8 @bitextract_undef_offset(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[X]], i32 undef
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 poison
;
%r = bitextract i8, b32 %x, i32 undef
ret i8 %r
@@ -95,8 +88,7 @@ define i8 @bitextract_undef_offset(b32 %x) {
define b32 @bitinsert_poison_offset(b32 %x, i8 %y) {
; CHECK-LABEL: define b32 @bitinsert_poison_offset(
; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 poison
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 poison
;
%r = bitinsert b32 %x, i8 %y, i32 poison
ret b32 %r
@@ -105,8 +97,7 @@ define b32 @bitinsert_poison_offset(b32 %x, i8 %y) {
define b32 @bitinsert_undef_offset(b32 %x, i8 %y) {
; CHECK-LABEL: define b32 @bitinsert_undef_offset(
; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 undef
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 poison
;
%r = bitinsert b32 %x, i8 %y, i32 undef
ret b32 %r
@@ -115,8 +106,7 @@ define b32 @bitinsert_undef_offset(b32 %x, i8 %y) {
define b32 @bitextract_full_width(b32 %x) {
; CHECK-LABEL: define b32 @bitextract_full_width(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitextract b32, b32 [[X]], i32 0
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 [[X]]
;
%r = bitextract b32, b32 %x, i32 0
ret b32 %r
@@ -136,8 +126,7 @@ define i32 @neg_bitextract_full_width_i32(b32 %x) {
define b32 @bitinsert_full_width(b32 %x, b32 %y) {
; CHECK-LABEL: define b32 @bitinsert_full_width(
; CHECK-SAME: b32 [[X:%.*]], b32 [[Y:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b32 [[Y]], i32 0
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 [[Y]]
;
%r = bitinsert b32 %x, b32 %y, i32 0
ret b32 %r
@@ -157,9 +146,7 @@ define b32 @neg_bitinsert_full_width_i32(b32 %x, i32 %y) {
define i8 @bitextract_bitinsert_i8(b32 %x, i8 %y) {
; CHECK-LABEL: define i8 @bitextract_bitinsert_i8(
; 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
@@ -169,9 +156,7 @@ define i8 @bitextract_bitinsert_i8(b32 %x, i8 %y) {
define b8 @bitextract_bitinsert_b8(b32 %x, b8 %y) {
; CHECK-LABEL: define b8 @bitextract_bitinsert_b8(
; CHECK-SAME: b32 [[X:%.*]], b8 [[Y:%.*]]) {
-; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], b8 [[Y]], i32 8
-; CHECK-NEXT: [[R:%.*]] = bitextract b8, b32 [[I]], i32 8
-; CHECK-NEXT: ret b8 [[R]]
+; CHECK-NEXT: ret b8 [[Y]]
;
%i = bitinsert b32 %x, b8 %y, i32 8
%r = bitextract b8, b32 %i, i32 8
@@ -181,9 +166,7 @@ define b8 @bitextract_bitinsert_b8(b32 %x, b8 %y) {
define float @bitextract_bitinsert_float(b64 %x, float %y) {
; CHECK-LABEL: define float @bitextract_bitinsert_float(
; CHECK-SAME: b64 [[X:%.*]], float [[Y:%.*]]) {
-; CHECK-NEXT: [[I:%.*]] = bitinsert b64 [[X]], float [[Y]], i32 32
-; CHECK-NEXT: [[R:%.*]] = bitextract float, b64 [[I]], i32 32
-; CHECK-NEXT: ret float [[R]]
+; CHECK-NEXT: ret float [[Y]]
;
%i = bitinsert b64 %x, float %y, i32 32
%r = bitextract float, b64 %i, i32 32
@@ -193,9 +176,7 @@ define float @bitextract_bitinsert_float(b64 %x, float %y) {
define ptr @bitextract_bitinsert_ptr(b128 %x, ptr %y) {
; CHECK-LABEL: define ptr @bitextract_bitinsert_ptr(
; CHECK-SAME: b128 [[X:%.*]], ptr [[Y:%.*]]) {
-; CHECK-NEXT: [[I:%.*]] = bitinsert b128 [[X]], ptr [[Y]], i32 64
-; CHECK-NEXT: [[R:%.*]] = bitextract ptr, b128 [[I]], i32 64
-; CHECK-NEXT: ret ptr [[R]]
+; CHECK-NEXT: ret ptr [[Y]]
;
%i = bitinsert b128 %x, ptr %y, i32 64
%r = bitextract ptr, b128 %i, i32 64
@@ -205,9 +186,7 @@ define ptr @bitextract_bitinsert_ptr(b128 %x, ptr %y) {
define i8 @bitextract_bitinsert_variable_offset(b32 %x, i8 %y, i32 %n) {
; CHECK-LABEL: define i8 @bitextract_bitinsert_variable_offset(
; CHECK-SAME: b32 [[X:%.*]], i8 [[Y:%.*]], i32 [[N:%.*]]) {
-; CHECK-NEXT: [[I:%.*]] = bitinsert b32 [[X]], i8 [[Y]], i32 [[N]]
-; CHECK-NEXT: [[R:%.*]] = bitextract i8, b32 [[I]], i32 [[N]]
-; CHECK-NEXT: ret i8 [[R]]
+; CHECK-NEXT: ret i8 [[Y]]
;
%i = bitinsert b32 %x, i8 %y, i32 %n
%r = bitextract i8, b32 %i, i32 %n
@@ -243,9 +222,7 @@ define b8 @neg_bitextract_bitinsert_other_type(b32 %x, i8 %y) {
define b32 @bitinsert_bitextract_b8(b32 %x) {
; CHECK-LABEL: define b32 @bitinsert_bitextract_b8(
; CHECK-SAME: b32 [[X:%.*]]) {
-; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[X]], i32 8
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 8
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 [[X]]
;
%e = bitextract b8, b32 %x, i32 8
%r = bitinsert b32 %x, b8 %e, i32 8
@@ -255,9 +232,7 @@ define b32 @bitinsert_bitextract_b8(b32 %x) {
define b32 @bitinsert_bitextract_variable_offset(b32 %x, i32 %n) {
; CHECK-LABEL: define b32 @bitinsert_bitextract_variable_offset(
; CHECK-SAME: b32 [[X:%.*]], i32 [[N:%.*]]) {
-; CHECK-NEXT: [[E:%.*]] = bitextract b8, b32 [[X]], i32 [[N]]
-; CHECK-NEXT: [[R:%.*]] = bitinsert b32 [[X]], b8 [[E]], i32 [[N]]
-; CHECK-NEXT: ret b32 [[R]]
+; CHECK-NEXT: ret b32 [[X]]
;
%e = bitextract b8, b32 %x, i32 %n
%r = bitinsert b32 %x, b8 %e, i32 %n
diff --git a/llvm/unittests/IR/PatternMatch.cpp b/llvm/unittests/IR/PatternMatch.cpp
index 7bf92f7761a51..f4057b4073b5a 100644
--- a/llvm/unittests/IR/PatternMatch.cpp
+++ b/llvm/unittests/IR/PatternMatch.cpp
@@ -1545,6 +1545,35 @@ TEST_F(PatternMatchTest, VectorOps) {
EXPECT_TRUE(A == Val);
}
+TEST_F(PatternMatchTest, BitOps) {
+ // BI = bitinsert b32 0, i8 1, i32 8
+ // BE = bitextract i8, b32 %BI, i32 8
+ Value *Base = ConstantByte::get(IRB.getByte32Ty(), 0);
+ Value *Val = IRB.getInt8(1);
+ Value *Off = IRB.getInt32(8);
+ Value *BI = IRB.CreateBitInsert(Base, Val, Off);
+ Value *BE = IRB.CreateBitExtract(IRB.getInt8Ty(), BI, Off);
+
+ Value *A, *B, *C;
+ EXPECT_TRUE(match(BI, m_BitInsert(m_Value(A), m_Value(B), m_Value(C))));
+ EXPECT_EQ(A, Base);
+ EXPECT_EQ(B, Val);
+ EXPECT_EQ(C, Off);
+ EXPECT_TRUE(match(
+ BI, m_BitInsert(m_Specific(Base), m_SpecificInt(1), m_SpecificInt(8))));
+ EXPECT_FALSE(match(BI, m_BitInsert(m_Value(), m_Value(), m_Zero())));
+
+ EXPECT_TRUE(match(BE, m_BitExtract(m_Value(A), m_Value(B))));
+ EXPECT_EQ(A, BI);
+ EXPECT_EQ(B, Off);
+ EXPECT_FALSE(match(BE, m_BitExtract(m_Value(), m_Zero())));
+
+ EXPECT_TRUE(
+ match(BE, m_BitExtract(m_BitInsert(m_Value(), m_Value(), m_Value()),
+ m_SpecificInt(8))));
+ EXPECT_FALSE(match(BI, m_BitExtract(m_Value(), m_Value())));
+}
+
TEST_F(PatternMatchTest, UndefPoisonMix) {
Type *ScalarTy = IRB.getInt8Ty();
ArrayType *ArrTy = ArrayType::get(ScalarTy, 2);
>From 3f3017f4d96fb9df909e303942fe130c676d5322 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Tue, 6 Oct 2026 12:58:31 +0100
Subject: [PATCH 3/3] Fix CI
---
llvm/lib/Analysis/InstructionSimplify.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 3923cfbdff7b6..247d2d771ca46 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -5640,7 +5640,7 @@ static Value *simplifyBitInsertInst(Value *Base, Value *Val, Value *Offset,
auto *CVal = dyn_cast<Constant>(Val);
auto *COffset = dyn_cast<Constant>(Offset);
if (CBase && CVal && COffset)
- if (Constant *C = ConstantFoldBitInsertInstruction(CBase, CVal, COffset))
+ if (Constant *C = ConstantFoldBitInsertOperands(CBase, CVal, COffset, Q.DL))
return C;
// bitinsert x, y, poison/undef/out_of_range -> poison
@@ -5674,7 +5674,7 @@ static Value *simplifyBitExtractInst(Type *Ty, Value *Src, Value *Offset,
auto *CSrc = dyn_cast<Constant>(Src);
auto *COffset = dyn_cast<Constant>(Offset);
if (CSrc && COffset)
- if (Constant *C = ConstantFoldBitExtractInstruction(Ty, CSrc, COffset))
+ if (Constant *C = ConstantFoldBitExtractOperands(Ty, CSrc, COffset, Q.DL))
return C;
// bitextract ty, x, poison/undef/out_of_range -> poison
More information about the llvm-branch-commits
mailing list