From 558fee5abc0c2ddc95da89aea219a9bc41c3a634 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Wed, 11 Mar 2026 23:25:19 +0800 Subject: [PATCH 1/9] Pre-commit test --- .../InstCombine/bitcast-splat-mul.ll | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll diff --git a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll new file mode 100644 index 0000000000000..215f2c11a0686 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll @@ -0,0 +1,106 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i32 @issue185694_i1(i64 %arg0, i64 %arg1) { +; CHECK-LABEL: define i32 @issue185694_i1( +; CHECK-SAME: i64 [[ARG0:%.*]], i64 [[ARG1:%.*]]) { +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[ARG1]], [[ARG0]] +; CHECK-NEXT: [[INS:%.*]] = insertelement <4 x i1> poison, i1 [[CMP]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i1> [[INS]], <4 x i1> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[NEG:%.*]] = bitcast <4 x i1> [[SPLAT]] to i4 +; CHECK-NEXT: [[EXT:%.*]] = zext i4 [[NEG]] to i32 +; CHECK-NEXT: ret i32 [[EXT]] +; + %cmp = icmp eq i64 %arg1, %arg0 + %ins = insertelement <4 x i1> poison, i1 %cmp, i64 0 + %splat = shufflevector <4 x i1> %ins, <4 x i1> poison, <4 x i32> zeroinitializer + %bc = bitcast <4 x i1> %splat to i4 + %ext = zext i4 %bc to i32 + ret i32 %ext +} + +define i32 @splat_i8_nonzero_lane(i8 %x) { +; CHECK-LABEL: define i32 @splat_i8_nonzero_lane( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i8> poison, i8 [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i8> [[TMP1]], <4 x i8> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[MUL:%.*]] = bitcast <4 x i8> [[SPLAT]] to i32 +; CHECK-NEXT: ret i32 [[MUL]] +; + %ins = insertelement <4 x i8> poison, i8 %x, i64 2 + %splat = shufflevector <4 x i8> %ins, <4 x i8> poison, <4 x i32> + %bc = bitcast <4 x i8> %splat to i32 + ret i32 %bc +} + +define i64 @zext_splat_i8_to_i64(i8 %x) { +; CHECK-LABEL: define i64 @zext_splat_i8_to_i64( +; CHECK-SAME: i8 [[X:%.*]]) { +; CHECK-NEXT: [[INS:%.*]] = insertelement <2 x i8> poison, i8 [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <2 x i8> [[INS]], <2 x i8> poison, <2 x i32> zeroinitializer +; CHECK-NEXT: [[MUL:%.*]] = bitcast <2 x i8> [[SPLAT]] to i16 +; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[MUL]] to i64 +; CHECK-NEXT: ret i64 [[EXT]] +; + %ins = insertelement <2 x i8> poison, i8 %x, i64 0 + %splat = shufflevector <2 x i8> %ins, <2 x i8> poison, <2 x i32> zeroinitializer + %bc = bitcast <2 x i8> %splat to i16 + %ext = zext i16 %bc to i64 + ret i64 %ext +} + +define i64 @splat_float(float %x) { +; CHECK-LABEL: define i64 @splat_float( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = insertelement <2 x float> poison, float [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <2 x float> [[TMP1]], <2 x float> poison, <2 x i32> zeroinitializer +; CHECK-NEXT: [[BC:%.*]] = bitcast <2 x float> [[SPLAT]] to i64 +; CHECK-NEXT: ret i64 [[BC]] +; + %ins = insertelement <2 x float> poison, float %x, i64 1 + %splat = shufflevector <2 x float> %ins, <2 x float> poison, <2 x i32> + %bc = bitcast <2 x float> %splat to i64 + ret i64 %bc +} + +define i128 @splat_float_to_i128(float %x) { +; CHECK-LABEL: define i128 @splat_float_to_i128( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x float> poison, float [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x float> [[TMP1]], <4 x float> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[BC:%.*]] = bitcast <4 x float> [[SPLAT]] to i128 +; CHECK-NEXT: ret i128 [[BC]] +; + %ins = insertelement <4 x float> poison, float %x, i64 3 + %splat = shufflevector <4 x float> %ins, <4 x float> poison, + <4 x i32> + %bc = bitcast <4 x float> %splat to i128 + ret i128 %bc +} + +define i128 @splat_i32_to_i128(i32 %x) { +; CHECK-LABEL: define i128 @splat_i32_to_i128( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i32> poison, i32 [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[MUL:%.*]] = bitcast <4 x i32> [[SPLAT]] to i128 +; CHECK-NEXT: ret i128 [[MUL]] +; + %ins = insertelement <4 x i32> poison, i32 %x, i64 1 + %splat = shufflevector <4 x i32> %ins, <4 x i32> poison, + <4 x i32> + %bc = bitcast <4 x i32> %splat to i128 + ret i128 %bc +} + +define i32 @nonsplat_shuffle(<4 x i8> %x) { +; CHECK-LABEL: define i32 @nonsplat_shuffle( +; CHECK-SAME: <4 x i8> [[X:%.*]]) { +; CHECK-NEXT: [[SHUF:%.*]] = shufflevector <4 x i8> [[X]], <4 x i8> poison, <4 x i32> +; CHECK-NEXT: [[BC:%.*]] = bitcast <4 x i8> [[SHUF]] to i32 +; CHECK-NEXT: ret i32 [[BC]] +; + %shuf = shufflevector <4 x i8> %x, <4 x i8> poison, <4 x i32> + %bc = bitcast <4 x i8> %shuf to i32 + ret i32 %bc +} From ec66c6640531384d1493c19b93183a1f4740e16e Mon Sep 17 00:00:00 2001 From: Camsyn Date: Wed, 11 Mar 2026 23:37:22 +0800 Subject: [PATCH 2/9] Fold splat-vec bitcast to mul x, C --- .../InstCombine/InstCombineCasts.cpp | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index 0cd2c09726a2d..cdd34d90c0a4a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -3083,6 +3083,57 @@ static Value *foldCopySignIdioms(BitCastInst &CI, return Builder.CreateCopySign(Builder.CreateBitCast(Y, FTy), X); } +// bitcast (shuf X, Y, splat_mask) to iN --> (zext x) * C +// where x is the splatted integer source element with bitwidth W and +// C = 1 + 2^W + 2^(2W) + ... = (2^N - 1)/(2^W - 1) +// E.g., +// x: i1, y = bitcast [x, x, x, x] --> y = x * 15 +// x: i8, y = bitcast [x, x, x, x] --> y = x * 16843009 +static Value *foldSplatShuffleToMul(ShuffleVectorInst *Shuf, IntegerType *DstTy, + InstCombiner::BuilderTy &Builder) { + auto *ShufTy = dyn_cast(Shuf->getType()); + if (!ShufTy) + return nullptr; + auto *EltTy = dyn_cast(ShufTy->getElementType()); + // Restrict this fold to integer splats. Reinterpreting a non-integer splat + // element as an integer and then multiplying by C is algebraically sound, but + // llvm-mca shows that it can generate worse code than keeping the + // splat-vector bitcast form. + if (!EltTy) + return nullptr; + ArrayRef Mask = Shuf->getShuffleMask(); + + // Check if this is a splat-shuffle with a valid index + if (!all_equal(Mask) || Mask[0] == PoisonMaskElem) + return nullptr; + + unsigned DstWidth = DstTy->getBitWidth(); + assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() && + "bitcast width mismatch"); + + unsigned SplatIndex = static_cast(Mask[0]); + Value *SplatSource = Shuf->getOperand(0); + unsigned NumElts = + cast(SplatSource->getType())->getNumElements(); + if (SplatIndex >= NumElts) { + SplatSource = Shuf->getOperand(1); + SplatIndex -= NumElts; + } + + assert(SplatIndex < + cast(SplatSource->getType())->getNumElements() && + "splat index must be within the selected shuffle source"); + + // bitcast (splat x) to integer is: + // y = x * C, where C = 1 + 2^W + 2^(2W) + ... + // and W is the source element width. + Value *Splat = + Builder.CreateExtractElement(SplatSource, Builder.getInt64(SplatIndex)); + APInt MulC = APInt::getSplat(DstWidth, APInt(EltTy->getBitWidth(), 1)); + Value *WideSplat = Builder.CreateZExt(Splat, DstTy); + return BinaryOperator::CreateMul(WideSplat, ConstantInt::get(DstTy, MulC)); +} + Instruction *InstCombinerImpl::visitBitCast(BitCastInst &CI) { // If the operands are integer typed then apply the integer transforms, // otherwise just apply the common ones. @@ -3161,8 +3212,16 @@ Instruction *InstCombinerImpl::visitBitCast(BitCastInst &CI) { } if (auto *Shuf = dyn_cast(Src)) { - // Okay, we have (bitcast (shuffle ..)). Check to see if this is - // a bitcast to a vector with the same # elts. + // Okay, we have (bitcast (shuffle ..)). + + // Check to see if the bitcast to iN is only user of a splat-shuffle. + // If so, try to fold `bitcast [x, ..., x] to iN` into `(zext x) * C` + if (auto *DstIntTy = dyn_cast(DestTy); + DstIntTy && Shuf->hasOneUser()) + if (Value *V = foldSplatShuffleToMul(Shuf, DstIntTy, Builder)) + return cast(V); + + // Check to see if this is a bitcast to a vector with the same # elts. Value *ShufOp0 = Shuf->getOperand(0); Value *ShufOp1 = Shuf->getOperand(1); auto ShufElts = cast(Shuf->getType())->getElementCount(); From af516b23101f11e87537f0862396594e1e0129dd Mon Sep 17 00:00:00 2001 From: Camsyn Date: Wed, 11 Mar 2026 23:37:35 +0800 Subject: [PATCH 3/9] Update tests --- .../InstCombine/bitcast-splat-mul.ll | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll index 215f2c11a0686..15d2de7f381ce 100644 --- a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll +++ b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll @@ -5,9 +5,7 @@ define i32 @issue185694_i1(i64 %arg0, i64 %arg1) { ; CHECK-LABEL: define i32 @issue185694_i1( ; CHECK-SAME: i64 [[ARG0:%.*]], i64 [[ARG1:%.*]]) { ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[ARG1]], [[ARG0]] -; CHECK-NEXT: [[INS:%.*]] = insertelement <4 x i1> poison, i1 [[CMP]], i64 0 -; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i1> [[INS]], <4 x i1> poison, <4 x i32> zeroinitializer -; CHECK-NEXT: [[NEG:%.*]] = bitcast <4 x i1> [[SPLAT]] to i4 +; CHECK-NEXT: [[NEG:%.*]] = sext i1 [[CMP]] to i4 ; CHECK-NEXT: [[EXT:%.*]] = zext i4 [[NEG]] to i32 ; CHECK-NEXT: ret i32 [[EXT]] ; @@ -22,9 +20,8 @@ define i32 @issue185694_i1(i64 %arg0, i64 %arg1) { define i32 @splat_i8_nonzero_lane(i8 %x) { ; CHECK-LABEL: define i32 @splat_i8_nonzero_lane( ; CHECK-SAME: i8 [[X:%.*]]) { -; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i8> poison, i8 [[X]], i64 0 -; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i8> [[TMP1]], <4 x i8> poison, <4 x i32> zeroinitializer -; CHECK-NEXT: [[MUL:%.*]] = bitcast <4 x i8> [[SPLAT]] to i32 +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i32 +; CHECK-NEXT: [[MUL:%.*]] = mul nuw i32 [[TMP1]], 16843009 ; CHECK-NEXT: ret i32 [[MUL]] ; %ins = insertelement <4 x i8> poison, i8 %x, i64 2 @@ -36,9 +33,8 @@ define i32 @splat_i8_nonzero_lane(i8 %x) { define i64 @zext_splat_i8_to_i64(i8 %x) { ; CHECK-LABEL: define i64 @zext_splat_i8_to_i64( ; CHECK-SAME: i8 [[X:%.*]]) { -; CHECK-NEXT: [[INS:%.*]] = insertelement <2 x i8> poison, i8 [[X]], i64 0 -; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <2 x i8> [[INS]], <2 x i8> poison, <2 x i32> zeroinitializer -; CHECK-NEXT: [[MUL:%.*]] = bitcast <2 x i8> [[SPLAT]] to i16 +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i16 +; CHECK-NEXT: [[MUL:%.*]] = mul nuw i16 [[TMP1]], 257 ; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[MUL]] to i64 ; CHECK-NEXT: ret i64 [[EXT]] ; @@ -81,9 +77,8 @@ define i128 @splat_float_to_i128(float %x) { define i128 @splat_i32_to_i128(i32 %x) { ; CHECK-LABEL: define i128 @splat_i32_to_i128( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i32> poison, i32 [[X]], i64 0 -; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <4 x i32> zeroinitializer -; CHECK-NEXT: [[MUL:%.*]] = bitcast <4 x i32> [[SPLAT]] to i128 +; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[X]] to i128 +; CHECK-NEXT: [[MUL:%.*]] = mul nuw i128 [[TMP1]], 79228162532711081671548469249 ; CHECK-NEXT: ret i128 [[MUL]] ; %ins = insertelement <4 x i32> poison, i32 %x, i64 1 From 58dd6bd677d432f7fb80a676d3a646617ee29107 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Thu, 12 Mar 2026 00:15:28 +0800 Subject: [PATCH 4/9] Update existing tests --- .../Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll | 10 ++++------ llvm/test/Transforms/InstCombine/icmp-bc-vec.ll | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll index 2d4e4920d551c..2341ad5df05b6 100644 --- a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll +++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll @@ -88,11 +88,7 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) { ; Make sure we don't try to fold if the compared-to constant isn't a splatted value define i1 @test_i8_nopattern(i8 %val) { ; CHECK-LABEL: @test_i8_nopattern( -; CHECK-NEXT: [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0 -; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer -; CHECK-NEXT: [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32 -; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647 -; CHECK-NEXT: ret i1 [[COND]] +; CHECK-NEXT: ret i1 false ; %insvec = insertelement <4 x i8> poison, i8 %val, i32 0 %vec = shufflevector <4 x i8> %insvec, <4 x i8> poison, <4 x i32> zeroinitializer @@ -117,7 +113,9 @@ define i1 @test_i8_ult_pattern(i8 %val) { define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) { ; CHECK-LABEL: @extending_shuffle_with_weird_types( ; CHECK-NEXT: [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = zext i9 [[TMP1]] to i27 +; CHECK-NEXT: [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657 ; CHECK-NEXT: ret i1 [[CMP]] ; %splat = shufflevector <2 x i9> %v, <2 x i9> poison, <3 x i32> zeroinitializer diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll index 9369d5564c3a2..92de9a2646cbb 100644 --- a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll +++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll @@ -88,11 +88,7 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) { ; Make sure we don't try to fold if the compared-to constant isn't a splatted value define i1 @test_i8_nopattern(i8 %val) { ; CHECK-LABEL: @test_i8_nopattern( -; CHECK-NEXT: [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0 -; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer -; CHECK-NEXT: [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32 -; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647 -; CHECK-NEXT: ret i1 [[COND]] +; CHECK-NEXT: ret i1 false ; %insvec = insertelement <4 x i8> undef, i8 %val, i32 0 %vec = shufflevector <4 x i8> %insvec, <4 x i8> undef, <4 x i32> zeroinitializer @@ -117,7 +113,9 @@ define i1 @test_i8_ult_pattern(i8 %val) { define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) { ; CHECK-LABEL: @extending_shuffle_with_weird_types( ; CHECK-NEXT: [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = zext i9 [[TMP1]] to i27 +; CHECK-NEXT: [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657 ; CHECK-NEXT: ret i1 [[CMP]] ; %splat = shufflevector <2 x i9> %v, <2 x i9> undef, <3 x i32> zeroinitializer From 8b8acc82e61aea9a07f50a727825fff3e8d733a2 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 7 Apr 2026 17:59:21 +0800 Subject: [PATCH 5/9] NFC fix adhering to reviews --- .../InstCombine/InstCombineCasts.cpp | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index cdd34d90c0a4a..c78d2330ede43 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -3083,15 +3083,24 @@ static Value *foldCopySignIdioms(BitCastInst &CI, return Builder.CreateCopySign(Builder.CreateBitCast(Y, FTy), X); } -// bitcast (shuf X, Y, splat_mask) to iN --> (zext x) * C -// where x is the splatted integer source element with bitwidth W and -// C = 1 + 2^W + 2^(2W) + ... = (2^N - 1)/(2^W - 1) -// E.g., -// x: i1, y = bitcast [x, x, x, x] --> y = x * 15 -// x: i8, y = bitcast [x, x, x, x] --> y = x * 16843009 -static Value *foldSplatShuffleToMul(ShuffleVectorInst *Shuf, IntegerType *DstTy, - InstCombiner::BuilderTy &Builder) { - auto *ShufTy = dyn_cast(Shuf->getType()); +/// bitcast (shuf X, Y, splat_mask) to iN --> (zext x) * C +/// where x is the splatted integer source element with bitwidth W and +/// C = 1 + 2^W + 2^(2W) + ... = (2^N - 1)/(2^W - 1) +/// E.g., +/// x: i1, y = bitcast [x, x, x, x] --> y = x * 15 +/// x: i8, y = bitcast [x, x, x, x] --> y = x * 16843009 +static Instruction *foldSplatShuffleToMul(const ShuffleVectorInst &Shuf, + IntegerType *DstTy, + InstCombiner::BuilderTy &Builder) { + // If Shuf has other user besides the bitcast, bail out. + if (!Shuf.hasOneUse()) + return nullptr; + + assert(isa(*Shuf.use_begin()) && + "The sole user of shuf must be a bitcast"); + + auto *ShufTy = dyn_cast(Shuf.getType()); + // Cannot support scalable vector. if (!ShufTy) return nullptr; auto *EltTy = dyn_cast(ShufTy->getElementType()); @@ -3101,7 +3110,7 @@ static Value *foldSplatShuffleToMul(ShuffleVectorInst *Shuf, IntegerType *DstTy, // splat-vector bitcast form. if (!EltTy) return nullptr; - ArrayRef Mask = Shuf->getShuffleMask(); + ArrayRef Mask = Shuf.getShuffleMask(); // Check if this is a splat-shuffle with a valid index if (!all_equal(Mask) || Mask[0] == PoisonMaskElem) @@ -3111,24 +3120,23 @@ static Value *foldSplatShuffleToMul(ShuffleVectorInst *Shuf, IntegerType *DstTy, assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() && "bitcast width mismatch"); + // Get the value to splat via the splat index. unsigned SplatIndex = static_cast(Mask[0]); - Value *SplatSource = Shuf->getOperand(0); + Value *SplatSource = Shuf.getOperand(0); unsigned NumElts = cast(SplatSource->getType())->getNumElements(); if (SplatIndex >= NumElts) { - SplatSource = Shuf->getOperand(1); + SplatSource = Shuf.getOperand(1); SplatIndex -= NumElts; } - assert(SplatIndex < - cast(SplatSource->getType())->getNumElements() && + assert(SplatIndex < NumElts && "splat index must be within the selected shuffle source"); // bitcast (splat x) to integer is: // y = x * C, where C = 1 + 2^W + 2^(2W) + ... // and W is the source element width. - Value *Splat = - Builder.CreateExtractElement(SplatSource, Builder.getInt64(SplatIndex)); + Value *Splat = Builder.CreateExtractElement(SplatSource, SplatIndex); APInt MulC = APInt::getSplat(DstWidth, APInt(EltTy->getBitWidth(), 1)); Value *WideSplat = Builder.CreateZExt(Splat, DstTy); return BinaryOperator::CreateMul(WideSplat, ConstantInt::get(DstTy, MulC)); @@ -3214,12 +3222,10 @@ Instruction *InstCombinerImpl::visitBitCast(BitCastInst &CI) { if (auto *Shuf = dyn_cast(Src)) { // Okay, we have (bitcast (shuffle ..)). - // Check to see if the bitcast to iN is only user of a splat-shuffle. - // If so, try to fold `bitcast [x, ..., x] to iN` into `(zext x) * C` - if (auto *DstIntTy = dyn_cast(DestTy); - DstIntTy && Shuf->hasOneUser()) - if (Value *V = foldSplatShuffleToMul(Shuf, DstIntTy, Builder)) - return cast(V); + // Try to fold `bitcast [x, ..., x] to iN` into `(zext x) * C` + if (auto *DstIntTy = dyn_cast(DestTy)) + if (Instruction *I = foldSplatShuffleToMul(*Shuf, DstIntTy, Builder)) + return I; // Check to see if this is a bitcast to a vector with the same # elts. Value *ShufOp0 = Shuf->getOperand(0); From 57fd4b9590b5556d6389f632f73b9833e407cbcb Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 7 Apr 2026 18:00:06 +0800 Subject: [PATCH 6/9] Check `DL.fitsInLegalInteger()` --- llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index c78d2330ede43..5e88690b69a64 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -3110,6 +3110,12 @@ static Instruction *foldSplatShuffleToMul(const ShuffleVectorInst &Shuf, // splat-vector bitcast form. if (!EltTy) return nullptr; + + // It would be less beneficial when the dest type is so large that it needs to + // be legalized in the backend. + if (!Shuf.getDataLayout().fitsInLegalInteger(DstTy->getBitWidth())) + return nullptr; + ArrayRef Mask = Shuf.getShuffleMask(); // Check if this is a splat-shuffle with a valid index From d44cdf01d5904b3b88e5d43ed4383e051c0af00b Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 7 Apr 2026 18:02:55 +0800 Subject: [PATCH 7/9] Minor adjustment --- llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index 5e88690b69a64..8951e29c44ad1 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -3111,9 +3111,12 @@ static Instruction *foldSplatShuffleToMul(const ShuffleVectorInst &Shuf, if (!EltTy) return nullptr; + const unsigned DstWidth = DstTy->getBitWidth(); + assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() && + "bitcast width mismatch"); // It would be less beneficial when the dest type is so large that it needs to // be legalized in the backend. - if (!Shuf.getDataLayout().fitsInLegalInteger(DstTy->getBitWidth())) + if (!Shuf.getDataLayout().fitsInLegalInteger(DstWidth)) return nullptr; ArrayRef Mask = Shuf.getShuffleMask(); @@ -3122,10 +3125,6 @@ static Instruction *foldSplatShuffleToMul(const ShuffleVectorInst &Shuf, if (!all_equal(Mask) || Mask[0] == PoisonMaskElem) return nullptr; - unsigned DstWidth = DstTy->getBitWidth(); - assert(DstWidth == ShufTy->getPrimitiveSizeInBits().getFixedValue() && - "bitcast width mismatch"); - // Get the value to splat via the splat index. unsigned SplatIndex = static_cast(Mask[0]); Value *SplatSource = Shuf.getOperand(0); From bc1f353aa6500aa3763989cded8a96ee7fdc30e1 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 7 Apr 2026 18:39:47 +0800 Subject: [PATCH 8/9] Fix incorrect assertion --- llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index 8951e29c44ad1..b472e412a73bd 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -3096,7 +3096,7 @@ static Instruction *foldSplatShuffleToMul(const ShuffleVectorInst &Shuf, if (!Shuf.hasOneUse()) return nullptr; - assert(isa(*Shuf.use_begin()) && + assert(isa(*Shuf.user_begin()) && "The sole user of shuf must be a bitcast"); auto *ShufTy = dyn_cast(Shuf.getType()); From 1082af9d5929b3d780ed3d122f763452168d8304 Mon Sep 17 00:00:00 2001 From: Camsyn Date: Tue, 7 Apr 2026 21:11:14 +0800 Subject: [PATCH 9/9] Update tests after adding guard of `DL.fitsInLegalInteger` --- .../Transforms/InstCombine/bitcast-splat-mul.ll | 17 ++++++++++------- .../InstCombine/icmp-bc-vec-inseltpoison.ll | 10 ++++++---- llvm/test/Transforms/InstCombine/icmp-bc-vec.ll | 10 ++++++---- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll index 15d2de7f381ce..ecd3b84897e7b 100644 --- a/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll +++ b/llvm/test/Transforms/InstCombine/bitcast-splat-mul.ll @@ -1,12 +1,15 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s +; splat-vec to mul is related to target arch. +; E.g., i256 mul is not efficient in x86_64. +target triple = "x86_64-pc-linux-gnu" + define i32 @issue185694_i1(i64 %arg0, i64 %arg1) { ; CHECK-LABEL: define i32 @issue185694_i1( ; CHECK-SAME: i64 [[ARG0:%.*]], i64 [[ARG1:%.*]]) { ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[ARG1]], [[ARG0]] -; CHECK-NEXT: [[NEG:%.*]] = sext i1 [[CMP]] to i4 -; CHECK-NEXT: [[EXT:%.*]] = zext i4 [[NEG]] to i32 +; CHECK-NEXT: [[EXT:%.*]] = select i1 [[CMP]], i32 15, i32 0 ; CHECK-NEXT: ret i32 [[EXT]] ; %cmp = icmp eq i64 %arg1, %arg0 @@ -33,9 +36,8 @@ define i32 @splat_i8_nonzero_lane(i8 %x) { define i64 @zext_splat_i8_to_i64(i8 %x) { ; CHECK-LABEL: define i64 @zext_splat_i8_to_i64( ; CHECK-SAME: i8 [[X:%.*]]) { -; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i16 -; CHECK-NEXT: [[MUL:%.*]] = mul nuw i16 [[TMP1]], 257 -; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[MUL]] to i64 +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[X]] to i64 +; CHECK-NEXT: [[EXT:%.*]] = mul nuw nsw i64 [[TMP1]], 257 ; CHECK-NEXT: ret i64 [[EXT]] ; %ins = insertelement <2 x i8> poison, i8 %x, i64 0 @@ -77,8 +79,9 @@ define i128 @splat_float_to_i128(float %x) { define i128 @splat_i32_to_i128(i32 %x) { ; CHECK-LABEL: define i128 @splat_i32_to_i128( ; CHECK-SAME: i32 [[X:%.*]]) { -; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[X]] to i128 -; CHECK-NEXT: [[MUL:%.*]] = mul nuw i128 [[TMP1]], 79228162532711081671548469249 +; CHECK-NEXT: [[TMP1:%.*]] = insertelement <4 x i32> poison, i32 [[X]], i64 0 +; CHECK-NEXT: [[SPLAT:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[MUL:%.*]] = bitcast <4 x i32> [[SPLAT]] to i128 ; CHECK-NEXT: ret i128 [[MUL]] ; %ins = insertelement <4 x i32> poison, i32 %x, i64 1 diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll index 2341ad5df05b6..2d4e4920d551c 100644 --- a/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll +++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec-inseltpoison.ll @@ -88,7 +88,11 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) { ; Make sure we don't try to fold if the compared-to constant isn't a splatted value define i1 @test_i8_nopattern(i8 %val) { ; CHECK-LABEL: @test_i8_nopattern( -; CHECK-NEXT: ret i1 false +; CHECK-NEXT: [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0 +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647 +; CHECK-NEXT: ret i1 [[COND]] ; %insvec = insertelement <4 x i8> poison, i8 %val, i32 0 %vec = shufflevector <4 x i8> %insvec, <4 x i8> poison, <4 x i32> zeroinitializer @@ -113,9 +117,7 @@ define i1 @test_i8_ult_pattern(i8 %val) { define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) { ; CHECK-LABEL: @extending_shuffle_with_weird_types( ; CHECK-NEXT: [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0 -; CHECK-NEXT: [[TMP2:%.*]] = zext i9 [[TMP1]] to i27 -; CHECK-NEXT: [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1 ; CHECK-NEXT: ret i1 [[CMP]] ; %splat = shufflevector <2 x i9> %v, <2 x i9> poison, <3 x i32> zeroinitializer diff --git a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll index 92de9a2646cbb..9369d5564c3a2 100644 --- a/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll +++ b/llvm/test/Transforms/InstCombine/icmp-bc-vec.ll @@ -88,7 +88,11 @@ define i1 @test_i8_pattern_3(<4 x i8> %invec) { ; Make sure we don't try to fold if the compared-to constant isn't a splatted value define i1 @test_i8_nopattern(i8 %val) { ; CHECK-LABEL: @test_i8_nopattern( -; CHECK-NEXT: ret i1 false +; CHECK-NEXT: [[INSVEC:%.*]] = insertelement <4 x i8> poison, i8 [[VAL:%.*]], i64 0 +; CHECK-NEXT: [[VEC:%.*]] = shufflevector <4 x i8> [[INSVEC]], <4 x i8> poison, <4 x i32> zeroinitializer +; CHECK-NEXT: [[CAST:%.*]] = bitcast <4 x i8> [[VEC]] to i32 +; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[CAST]], 1212696647 +; CHECK-NEXT: ret i1 [[COND]] ; %insvec = insertelement <4 x i8> undef, i8 %val, i32 0 %vec = shufflevector <4 x i8> %insvec, <4 x i8> undef, <4 x i32> zeroinitializer @@ -113,9 +117,7 @@ define i1 @test_i8_ult_pattern(i8 %val) { define i1 @extending_shuffle_with_weird_types(<2 x i9> %v) { ; CHECK-LABEL: @extending_shuffle_with_weird_types( ; CHECK-NEXT: [[TMP1:%.*]] = extractelement <2 x i9> [[V:%.*]], i64 0 -; CHECK-NEXT: [[TMP2:%.*]] = zext i9 [[TMP1]] to i27 -; CHECK-NEXT: [[CAST:%.*]] = mul nuw i27 [[TMP2]], 262657 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i27 [[CAST]], 262657 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i9 [[TMP1]], 1 ; CHECK-NEXT: ret i1 [[CMP]] ; %splat = shufflevector <2 x i9> %v, <2 x i9> undef, <3 x i32> zeroinitializer