https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/229125
>From 0de109f613563a21331070811ae430ad83b7c445 Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Mon, 5 Oct 2026 14:30:15 +0100 Subject: [PATCH 1/5] [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 067caa2f21903fdd2a31369f59bbf239cc71f41e Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Mon, 5 Oct 2026 14:30:16 +0100 Subject: [PATCH 2/5] [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 148b42ce425b2..678f1cf45c222 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 adb21e87203156f163b918015fe47b7f44e0bbb3 Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Tue, 6 Oct 2026 12:58:31 +0100 Subject: [PATCH 3/5] 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 >From 261627793c9ab5bafc73a7ce6223fa6ac93a8449 Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Sat, 10 Oct 2026 18:48:22 +0100 Subject: [PATCH 4/5] [InstSimplify] Use `m_ConstantInt` for bit range offset --- llvm/lib/Analysis/InstructionSimplify.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 247d2d771ca46..9affb864c0225 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5626,9 +5626,9 @@ static bool isPoisonBitRange(Value *Offset, Type *Ty, Type *SrcTy, 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() > + uint64_t Off; + return match(Offset, m_ConstantInt(Off)) && + Off + Q.DL.getTypeSizeInBits(Ty).getFixedValue() > Q.DL.getTypeSizeInBits(SrcTy).getFixedValue(); } >From 3969e93896dcb4285a98efc8918772e43b6b603d Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Sat, 10 Oct 2026 18:49:04 +0100 Subject: [PATCH 5/5] [InstSimplify] Fold reinsert of non-byte `bitextract` --- llvm/lib/Analysis/InstructionSimplify.cpp | 8 ++---- .../InstSimplify/bitinsert-bitextract.ll | 28 ++++++------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 9affb864c0225..186f979705c4b 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5651,12 +5651,8 @@ static Value *simplifyBitInsertInst(Value *Base, Value *Val, Value *Offset, 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)))) + // bitinsert x, (bitextract ty, x, n), n -> x + if (match(Val, m_BitExtract(m_Specific(Base), m_Specific(Offset)))) return Base; return nullptr; diff --git a/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll index 91d721b006f2d..971ebea2d7b6e 100644 --- a/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll +++ b/llvm/test/Transforms/InstSimplify/bitinsert-bitextract.ll @@ -239,40 +239,30 @@ define b32 @bitinsert_bitextract_variable_offset(b32 %x, 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( +define b32 @bitinsert_bitextract_i8(b32 %x) { +; CHECK-LABEL: define b32 @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]] +; CHECK-NEXT: ret b32 [[X]] ; %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( +define b64 @bitinsert_bitextract_double(b64 %x) { +; CHECK-LABEL: define b64 @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]] +; CHECK-NEXT: ret b64 [[X]] ; %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( +define b64 @bitinsert_bitextract_ptr(b64 %x) { +; CHECK-LABEL: define b64 @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]] +; CHECK-NEXT: ret b64 [[X]] ; %e = bitextract ptr, b64 %x, i32 0 %r = bitinsert b64 %x, ptr %e, i32 0 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
