[llvm] [SandboxVec][LoadStoreVec] Support constant vectors of mixed types (PR #224890)

Anshil Gandhi via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 20 14:32:03 PDT 2026


https://github.com/gandhi56 updated https://github.com/llvm/llvm-project/pull/224890

>From 6c7dc5affbfe088ba943211c20592954d06a7ba3 Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <gandhi21299 at gmail.com>
Date: Sun, 20 Sep 2026 03:22:17 -0400
Subject: [PATCH] [SandboxVec][LoadStoreVec] Support constant vectors of mixed
 types

createConstantVector() previously packed the constant store operands
as-is, which only worked when every store had the same element type.
Take the lane type from VecUtils::getCombinedVectorTypeFor() instead and
reinterpret each constant's bits as that type, going through an integer
of matching width via ptrtoint/inttoptr/bitcast. Constants wider than a
lane (e.g. an i64 in an <N x i32>) are split across several lanes in
memory order. Bail out when a constant cannot be reinterpreted, such as
a non-integral pointer or a relocatable address that needs splitting.

Also flatten vector-typed ConstantPointerNull into per-lane nulls, and
bail out on the remaining vector constants such as poison rather than
packing them into the result.

Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
---
 .../SandboxVectorizer/Passes/LoadStoreVec.h   |  11 +-
 .../SandboxVectorizer/Passes/LoadStoreVec.cpp | 102 +++-
 .../Passes/LoadStoreVec/load_store_vec.ll     |   2 +-
 .../load_store_vec_mixed_types.ll             | 485 ++++++++++++++++++
 4 files changed, 593 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.h b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.h
index 7f2eb5cd032c1..55b36d60d8e35 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.h
@@ -61,9 +61,14 @@ class LLVM_ABI LoadStoreVec final : public RegionPass {
   /// or nullptr if \p Loads are not a vectorizable.
   LoadInst *createVectorLoad(BndlRef<Instruction *> Loads);
 
-  /// Builds a ConstantVector from per-lane constant store operands in \p
-  /// Constants. \returns the packed ConstantVector.
-  Value *createConstantVector(BndlRef<Value *> Constants);
+  /// Builds a ConstantVector with \p LaneTy elements from the constant store
+  /// operands in \p Constants, reinterpreting the bits of constants of other
+  /// types (e.g. float or ptr in an i32 vector) and splitting constants wider
+  /// than \p LaneTy into several lanes. Casts are folded, so nothing is
+  /// inserted at \p WhereIt. \returns the packed ConstantVector, or nullptr if
+  /// a constant cannot be reinterpreted.
+  Value *createConstantVector(ArrayRef<Value *> Constants, Type *LaneTy,
+                              BBIterator WhereIt);
 
   /// Vectorizes \p Stores and their operands if constants or consecutive
   /// loads. \returns true on success.
diff --git a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
index 3d4ffdcd34a9e..86d93b4cfdd73 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
@@ -82,7 +82,46 @@ LoadInst *LoadStoreVec::createVectorLoad(BndlRef<Instruction *> Loads) {
   return LoadInst::create(Ty, LdPtr, LdAlign, LdWhereIt, *Ctx, "VecIinitL");
 }
 
-Value *LoadStoreVec::createConstantVector(BndlRef<Value *> Operands) {
+/// Reinterprets the bits of \p C as \p DestTy, which must have the same size
+/// in \p DL. Goes through an integer of that size using ptrtoint / inttoptr /
+/// bitcast. \Returns nullptr if the result does not fold to a constant or if a
+/// non-integral pointer is involved.
+static Constant *reinterpretConstant(Constant *C, Type *DestTy,
+                                     BBIterator WhereIt, Context &Ctx,
+                                     const DataLayout &DL) {
+  Type *SrcTy = C->getType();
+  if (SrcTy == DestTy)
+    return C;
+  auto IsNonIntegralPtr = [&DL](Type *Ty) {
+    return Ty->isPointerTy() &&
+           DL.isNonIntegralAddressSpace(Ty->getPointerAddressSpace());
+  };
+  if (IsNonIntegralPtr(SrcTy) || IsNonIntegralPtr(DestTy))
+    return nullptr;
+  auto Cast = [&](Constant *V, Type *To,
+                  Instruction::Opcode Opc) -> Constant * {
+    if (V == nullptr)
+      return nullptr;
+    // Casts of constants fold, so nothing is inserted at WhereIt.
+    return dyn_cast<Constant>(
+        CastInst::create(To, Opc, V, WhereIt, Ctx, "VCast"));
+  };
+  Constant *AsInt = C;
+  if (!SrcTy->isIntegerTy()) {
+    Type *IntTy = IntegerType::get(Ctx, Utils::getNumBits(SrcTy, DL));
+    AsInt = Cast(C, IntTy,
+                 SrcTy->isPointerTy() ? Instruction::Opcode::PtrToInt
+                                      : Instruction::Opcode::BitCast);
+  }
+  if (DestTy->isIntegerTy())
+    return AsInt;
+  return Cast(AsInt, DestTy,
+              DestTy->isPointerTy() ? Instruction::Opcode::IntToPtr
+                                    : Instruction::Opcode::BitCast);
+}
+
+Value *LoadStoreVec::createConstantVector(ArrayRef<Value *> Operands,
+                                          Type *LaneTy, BBIterator WhereIt) {
   SmallVector<Constant *, 8> Constants;
   Constants.reserve(Operands.size());
   for (Value *Op : Operands) {
@@ -113,11 +152,61 @@ Value *LoadStoreVec::createConstantVector(BndlRef<Value *> Operands) {
                              ->getElementCount()
                              .getFixedValue()))
         Constants.push_back(Elm);
+    } else if (isa<ConstantPointerNull>(COp) &&
+               isa<VectorType>(COp->getType())) {
+      auto *VecTy = cast<FixedVectorType>(COp->getType());
+      auto *Elm =
+          ConstantPointerNull::get(cast<PointerType>(VecTy->getElementType()));
+      for ([[maybe_unused]] auto Cnt : seq<unsigned>(VecTy->getNumElements()))
+        Constants.push_back(Elm);
+    } else if (isa<VectorType>(COp->getType())) {
+      // TODO: Flatten the remaining vector constants, e.g. undef or poison.
+      return nullptr;
     } else {
       Constants.push_back(COp);
     }
   }
-  return ConstantVector::get(Constants);
+
+  // The stores may mix element types, e.g. i32 and float, in which case each
+  // constant has to be reinterpreted as the lane type. The lane type is the
+  // narrowest element type (see VecUtils::getCombinedVectorTypeFor()), so a
+  // wider constant, like an i64 in an <N x i32>, spans several lanes and is
+  // split in memory order.
+  unsigned LaneBits = Utils::getNumBits(LaneTy, *DL);
+  SmallVector<Constant *, 8> Lanes;
+  Lanes.reserve(Constants.size());
+  for (Constant *C : Constants) {
+    unsigned Bits = Utils::getNumBits(C->getType(), *DL);
+    if (Bits < LaneBits || Bits % LaneBits != 0)
+      return nullptr;
+
+    if (Bits == LaneBits) {
+      Constant *Lane = reinterpretConstant(C, LaneTy, WhereIt, *Ctx, *DL);
+      if (Lane == nullptr)
+        return nullptr;
+      Lanes.push_back(Lane);
+      continue;
+    }
+
+    // Only integer bit patterns can be split, so this bails out on constants
+    // like the address of a global.
+    auto *CI = dyn_cast_or_null<ConstantInt>(reinterpretConstant(
+        C, IntegerType::get(*Ctx, Bits), WhereIt, *Ctx, *DL));
+    if (CI == nullptr)
+      return nullptr;
+    const APInt &Val = CI->getValue();
+    unsigned NumLanes = Bits / LaneBits;
+    for (unsigned Idx : seq<unsigned>(NumLanes)) {
+      unsigned Part = DL->isLittleEndian() ? Idx : NumLanes - 1 - Idx;
+      Constant *Piece =
+          ConstantInt::get(*Ctx, Val.extractBits(LaneBits, Part * LaneBits));
+      Constant *Lane = reinterpretConstant(Piece, LaneTy, WhereIt, *Ctx, *DL);
+      if (Lane == nullptr)
+        return nullptr;
+      Lanes.push_back(Lane);
+    }
+  }
+  return ConstantVector::get(Lanes);
 }
 
 bool LoadStoreVec::vectorizeStores(BndlRef<Instruction *> Stores, Region &Rgn) {
@@ -168,7 +257,14 @@ bool LoadStoreVec::vectorizeStores(BndlRef<Instruction *> Stores, Region &Rgn) {
       return false;
     }
   } else if (AllConstants) {
-    VecOp = createConstantVector(Operands);
+    auto *VecTy =
+        cast<FixedVectorType>(VecUtils::getCombinedVectorTypeFor(Stores, *DL));
+    VecOp = createConstantVector(Operands, VecTy->getElementType(),
+                                 VecUtils::getLowest(Stores)->getIterator());
+    if (VecOp == nullptr) {
+      Ctx->accept();
+      return false;
+    }
   }
 
   // Generate vector store.
diff --git a/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec.ll b/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec.ll
index 4708871dee4fd..67dc7a0ab059e 100644
--- a/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec.ll
+++ b/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec.ll
@@ -301,7 +301,7 @@ define void @load_store_vec_constants(ptr %ptr) {
 ; CHECK-LABEL: define void @load_store_vec_constants(
 ; CHECK-SAME: ptr [[PTR:%.*]]) {
 ; CHECK-NEXT:    [[PTR0:%.*]] = getelementptr i8, ptr [[PTR]], i32 0
-; CHECK-NEXT:    store <3 x i8> <i8 42, i8 43, i8 44>, ptr [[PTR0]], align 1, !sandboxvec [[META15:![0-9]+]]
+; CHECK-NEXT:    store <4 x i8> <i8 42, i8 43, i8 0, i8 44>, ptr [[PTR0]], align 1, !sandboxvec [[META15:![0-9]+]]
 ; CHECK-NEXT:    ret void
 ;
   %ptr0 = getelementptr i8, ptr %ptr, i32 0
diff --git a/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec_mixed_types.ll b/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec_mixed_types.ll
index ea7e70f839ed0..1cb43f93a7ffb 100644
--- a/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec_mixed_types.ll
+++ b/llvm/test/Transforms/SandboxVectorizer/Passes/LoadStoreVec/load_store_vec_mixed_types.ll
@@ -1,6 +1,13 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=sandbox-vectorizer -sbvec-passes="seed-collection(enable-diff-types)<load-store-vec>" -sbvec-vec-reg-bits=1024 %s -S | FileCheck %s
 
+; The datalayout only marks addrspace(5) as non-integral; everything else is
+; the default, so it does not affect the rest of the tests.
+target datalayout = "e-ni:5"
+
+ at g = external global i32
+ at arr = external global [4 x i32]
+
 ; Check that we vectorize mixed ints and floats.
 define void @mixed_int_float(ptr %ptr0) {
 ; CHECK-LABEL: define void @mixed_int_float(
@@ -383,6 +390,453 @@ define void @mixed_dboule_pointer_with_four_external_users(ptr %ptr, ptr %ptrExt
   ret void
 }
 
+; ===----------------------------------------------------------------------=== ;
+; ===            Constant Operands: Same Width As The Lane                 === ;
+; ===----------------------------------------------------------------------=== ;
+
+; The lane type is the narrowest store type, i32 here, so the float constant is
+; bitcast to i32.
+define amdgpu_kernel void @merge_global_store_2_constants_i32_f32(ptr addrspace(1) %out) {
+; CHECK-LABEL: define amdgpu_kernel void @merge_global_store_2_constants_i32_f32(
+; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
+; CHECK-NEXT:    store <2 x i32> <i32 456, i32 1065353216>, ptr addrspace(1) [[OUT]], align 1, !sandboxvec [[META18:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %out.gep.1 = getelementptr i32, ptr addrspace(1) %out, i32 1
+  store float 1.0, ptr addrspace(1) %out.gep.1
+  store i32 456, ptr addrspace(1) %out
+  ret void
+}
+
+; Same widths, but now the float comes first so the lane type is float and it is
+; the integer constant that gets reinterpreted.
+define void @const_int_reinterpreted_as_float_lane(ptr %ptr) {
+; CHECK-LABEL: define void @const_int_reinterpreted_as_float_lane(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x float> <float 1.000000e+00, float 6.389920e-43>, ptr [[PTR]], align 1, !sandboxvec [[META19:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  store float 1.0, ptr %ptr
+  store i32 456, ptr %ptr.4
+  ret void
+}
+
+; A pointer constant in an integer lane goes through ptrtoint. The address of a
+; global does not fold to a ConstantInt, but the ConstantExpr is still a valid
+; vector element as long as it does not have to be split.
+define void @const_global_ptr_in_int_lane(ptr %ptr) {
+; CHECK-LABEL: define void @const_global_ptr_in_int_lane(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x i64> <i64 5, i64 ptrtoint (ptr @g to i64)>, ptr [[PTR]], align 1, !sandboxvec [[META20:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i64 5, ptr %ptr
+  store ptr @g, ptr %ptr.8
+  ret void
+}
+
+; A pointer constant in a float lane goes through ptrtoint + bitcast.
+define void @const_global_ptr_in_double_lane(ptr %ptr) {
+; CHECK-LABEL: define void @const_global_ptr_in_double_lane(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x double> <double 1.000000e+00, double bitcast (i64 ptrtoint (ptr @g to i64) to double)>, ptr [[PTR]], align 1, !sandboxvec [[META21:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store double 1.0, ptr %ptr
+  store ptr @g, ptr %ptr.8
+  ret void
+}
+
+; A float constant in a pointer lane goes through bitcast + inttoptr.
+define void @const_double_in_ptr_lane(ptr %ptr) {
+; CHECK-LABEL: define void @const_double_in_ptr_lane(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x ptr> <ptr null, ptr inttoptr (i64 4607182418800017408 to ptr)>, ptr [[PTR]], align 1, !sandboxvec [[META22:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store ptr null, ptr %ptr
+  store double 1.0, ptr %ptr.8
+  ret void
+}
+
+; A ptrtoint ConstantExpr operand.
+define void @const_ptrtoint_constexpr(ptr %ptr) {
+; CHECK-LABEL: define void @const_ptrtoint_constexpr(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x double> <double 1.000000e+00, double bitcast (i64 ptrtoint (ptr @g to i64) to double)>, ptr [[PTR]], align 1, !sandboxvec [[META23:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store double 1.0, ptr %ptr
+  store i64 ptrtoint (ptr @g to i64), ptr %ptr.8
+  ret void
+}
+
+; A getelementptr ConstantExpr operand.
+define void @const_gep_constexpr(ptr %ptr) {
+; CHECK-LABEL: define void @const_gep_constexpr(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x double> <double 1.000000e+00, double bitcast (i64 ptrtoint (ptr getelementptr (i32, ptr @arr, i64 2) to i64) to double)>, ptr [[PTR]], align 1, !sandboxvec [[META24:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store double 1.0, ptr %ptr
+  store ptr getelementptr (i32, ptr @arr, i64 2), ptr %ptr.8
+  ret void
+}
+
+define void @const_scalar_poison(ptr %ptr) {
+; CHECK-LABEL: define void @const_scalar_poison(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <2 x float> <float 1.000000e+00, float poison>, ptr [[PTR]], align 1, !sandboxvec [[META25:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  store float 1.0, ptr %ptr
+  store i32 poison, ptr %ptr.4
+  ret void
+}
+
+; ===----------------------------------------------------------------------=== ;
+; ===              Constant Operands: Wider Than The Lane                  === ;
+; ===----------------------------------------------------------------------=== ;
+
+; 0x0123456789ABCDEF splits into 0x89ABCDEF then 0x01234567, i.e. memory order.
+define void @const_i64_split_into_i32_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_i64_split_into_i32_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> <i32 1, i32 2, i32 -1985229329, i32 19088743>, ptr [[PTR]], align 1, !sandboxvec [[META26:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i32 1, ptr %ptr
+  store i32 2, ptr %ptr.4
+  store i64 81985529216486895, ptr %ptr.8
+  ret void
+}
+
+; double 1.0 is 0x3FF0000000000000, so the low lane is 0 and the high one is
+; 0x3FF00000.
+define void @const_double_split_into_i32_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_double_split_into_i32_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> <i32 1, i32 2, i32 0, i32 1072693248>, ptr [[PTR]], align 1, !sandboxvec [[META27:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i32 1, ptr %ptr
+  store i32 2, ptr %ptr.4
+  store double 1.0, ptr %ptr.8
+  ret void
+}
+
+; A null pointer splits into zero lanes.
+define void @const_null_ptr_split_into_i32_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_null_ptr_split_into_i32_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> <i32 1, i32 2, i32 0, i32 0>, ptr [[PTR]], align 1, !sandboxvec [[META28:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i32 1, ptr %ptr
+  store i32 2, ptr %ptr.4
+  store ptr null, ptr %ptr.8
+  ret void
+}
+
+; Each half of the i128 is turned back into a pointer with inttoptr.
+define void @const_i128_split_into_ptr_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_i128_split_into_ptr_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x ptr> <ptr null, ptr null, ptr inttoptr (i64 5 to ptr), ptr null>, ptr [[PTR]], align 1, !sandboxvec [[META29:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  %ptr.16 = getelementptr i8, ptr %ptr, i64 16
+  store ptr null, ptr %ptr
+  store ptr null, ptr %ptr.8
+  store i128 5, ptr %ptr.16
+  ret void
+}
+
+; i32 7 splits into the halves 7 and 0, which are then bitcast to half.
+define void @const_i32_split_into_half_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_i32_split_into_half_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x half> <half 1.000000e+00, half 2.000000e+00, half 4.172330e-07, half 0.000000e+00>, ptr [[PTR]], align 1, !sandboxvec [[META30:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.2 = getelementptr i8, ptr %ptr, i64 2
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  store half 1.0, ptr %ptr
+  store half 2.0, ptr %ptr.2
+  store i32 7, ptr %ptr.4
+  ret void
+}
+
+; float 3.0 is 0x40400000, so the bfloat lanes are 0x0000 then 0x4040 (3.0).
+define void @const_float_split_into_bfloat_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_float_split_into_bfloat_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x bfloat> <bfloat 1.000000e+00, bfloat 2.000000e+00, bfloat 0.000000e+00, bfloat 3.000000e+00>, ptr [[PTR]], align 1, !sandboxvec [[META31:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.2 = getelementptr i8, ptr %ptr, i64 2
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  store bfloat 1.0, ptr %ptr
+  store bfloat 2.0, ptr %ptr.2
+  store float 3.0, ptr %ptr.4
+  ret void
+}
+
+; i16 772 is 0x0304, so the i8 lanes are 4 then 3.
+define void @const_i16_split_into_i8_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_i16_split_into_i8_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i8> <i8 1, i8 2, i8 4, i8 3>, ptr [[PTR]], align 1, !sandboxvec [[META32:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.2 = getelementptr i8, ptr %ptr, i64 2
+  store <2 x i8> <i8 1, i8 2>, ptr %ptr
+  store i16 772, ptr %ptr.2
+  ret void
+}
+
+; Three different 64-bit types plus a <2 x float>, all packed into float lanes.
+define void @const_mixed_widths_in_float_lanes(ptr %ptr) {
+; CHECK-LABEL: define void @const_mixed_widths_in_float_lanes(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <8 x float> <float 1.401300e-45, float 0.000000e+00, float 0.000000e+00, float 2.000000e+00, float 0.000000e+00, float 0.000000e+00, float 3.000000e+00, float 4.000000e+00>, ptr [[PTR]], align 1, !sandboxvec [[META33:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  %ptr.16 = getelementptr i8, ptr %ptr, i64 16
+  %ptr.24 = getelementptr i8, ptr %ptr, i64 24
+  store i64 1, ptr %ptr
+  store double 2.0, ptr %ptr.8
+  store ptr null, ptr %ptr.16
+  store <2 x float> <float 3.0, float 4.0>, ptr %ptr.24
+  ret void
+}
+
+; ===----------------------------------------------------------------------=== ;
+; ===               Constant Operands: Vector Constants                === ;
+; ===----------------------------------------------------------------------=== ;
+
+; ConstantDataVector operands are flattened element by element.
+define void @const_data_vectors(ptr %ptr) {
+; CHECK-LABEL: define void @const_data_vectors(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> <i32 1, i32 2, i32 1077936128, i32 1082130432>, ptr [[PTR]], align 1, !sandboxvec [[META34:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x i32> <i32 1, i32 2>, ptr %ptr
+  store <2 x float> <float 3.0, float 4.0>, ptr %ptr.8
+  ret void
+}
+
+; ConstantAggregateZero operands are flattened into per-lane zeros.
+define void @const_aggregate_zero(ptr %ptr) {
+; CHECK-LABEL: define void @const_aggregate_zero(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x float> <float 0.000000e+00, float 0.000000e+00, float 7.006490e-45, float 8.407790e-45>, ptr [[PTR]], align 1, !sandboxvec [[META35:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x float> zeroinitializer, ptr %ptr
+  store <2 x i32> <i32 5, i32 6>, ptr %ptr.8
+  ret void
+}
+
+define void @const_all_aggregate_zero(ptr %ptr) {
+; CHECK-LABEL: define void @const_all_aggregate_zero(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> zeroinitializer, ptr [[PTR]], align 1, !sandboxvec [[META36:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x i32> zeroinitializer, ptr %ptr
+  store <2 x float> zeroinitializer, ptr %ptr.8
+  ret void
+}
+
+; A vector-typed ConstantInt splat is expanded into one lane per element.
+define void @const_splat_int_vector(ptr %ptr) {
+; CHECK-LABEL: define void @const_splat_int_vector(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x i32> <i32 7, i32 7, i32 1077936128, i32 1082130432>, ptr [[PTR]], align 1, !sandboxvec [[META37:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x i32> splat (i32 7), ptr %ptr
+  store <2 x float> <float 3.0, float 4.0>, ptr %ptr.8
+  ret void
+}
+
+; Same for a vector-typed ConstantFP splat.
+define void @const_splat_fp_vector(ptr %ptr) {
+; CHECK-LABEL: define void @const_splat_fp_vector(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x float> <float 2.000000e+00, float 2.000000e+00, float 1.261170e-44, float 1.401300e-44>, ptr [[PTR]], align 1, !sandboxvec [[META38:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x float> splat (float 2.0), ptr %ptr
+  store <2 x i32> <i32 9, i32 10>, ptr %ptr.8
+  ret void
+}
+
+; A non-uniform vector of pointers is a ConstantVector (a ConstantAggregate).
+define void @const_vector_of_ptrs(ptr %ptr) {
+; CHECK-LABEL: define void @const_vector_of_ptrs(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    store <4 x ptr> <ptr @g, ptr null, ptr inttoptr (i64 3 to ptr), ptr inttoptr (i64 4 to ptr)>, ptr [[PTR]], align 1, !sandboxvec [[META39:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.16 = getelementptr i8, ptr %ptr, i64 16
+  %ptr.24 = getelementptr i8, ptr %ptr, i64 24
+  store <2 x ptr> <ptr @g, ptr null>, ptr %ptr
+  store i64 3, ptr %ptr.16
+  store i64 4, ptr %ptr.24
+  ret void
+}
+
+; A zero vector of pointers is a vector-typed ConstantPointerNull, which has to
+; be split into per-lane nulls.
+define void @store_null_ptr_vectors(ptr %out) {
+; CHECK-LABEL: define void @store_null_ptr_vectors(
+; CHECK-SAME: ptr [[OUT:%.*]]) {
+; CHECK-NEXT:    store <4 x ptr> splat (ptr null), ptr [[OUT]], align 1, !sandboxvec [[META40:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %out.1 = getelementptr <2 x ptr>, ptr %out, i64 1
+  store <2 x ptr> zeroinitializer, ptr %out
+  store <2 x ptr> zeroinitializer, ptr %out.1
+  ret void
+}
+
+; ===----------------------------------------------------------------------=== ;
+; ===                          Negative Tests                          === ;
+; ===----------------------------------------------------------------------=== ;
+
+; Same for a vector-typed poison.
+define void @const_vector_poison(ptr %ptr) {
+; CHECK-LABEL: define void @const_vector_poison(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    [[PTR_8:%.*]] = getelementptr i8, ptr [[PTR]], i64 8
+; CHECK-NEXT:    store <2 x i32> poison, ptr [[PTR]], align 8, !sandboxvec [[META41:![0-9]+]]
+; CHECK-NEXT:    store <2 x float> <float 3.000000e+00, float 4.000000e+00>, ptr [[PTR_8]], align 8, !sandboxvec [[META41]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store <2 x i32> poison, ptr %ptr
+  store <2 x float> <float 3.0, float 4.0>, ptr %ptr.8
+  ret void
+}
+
+; The address of a global is not a known bit pattern, so it cannot be split
+; across two i32 lanes. Only the two i32 stores are vectorized.
+define void @const_global_ptr_needs_split(ptr %ptr) {
+; CHECK-LABEL: define void @const_global_ptr_needs_split(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    [[PTR_8:%.*]] = getelementptr i8, ptr [[PTR]], i64 8
+; CHECK-NEXT:    store <2 x i32> <i32 1, i32 2>, ptr [[PTR]], align 1, !sandboxvec [[META42:![0-9]+]]
+; CHECK-NEXT:    store ptr @g, ptr [[PTR_8]], align 8, !sandboxvec [[META43:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i32 1, ptr %ptr
+  store i32 2, ptr %ptr.4
+  store ptr @g, ptr %ptr.8
+  ret void
+}
+
+; Same for a getelementptr ConstantExpr.
+define void @const_gep_constexpr_needs_split(ptr %ptr) {
+; CHECK-LABEL: define void @const_gep_constexpr_needs_split(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    [[PTR_8:%.*]] = getelementptr i8, ptr [[PTR]], i64 8
+; CHECK-NEXT:    store <2 x i32> <i32 1, i32 2>, ptr [[PTR]], align 1, !sandboxvec [[META44:![0-9]+]]
+; CHECK-NEXT:    store ptr getelementptr (i32, ptr @arr, i64 2), ptr [[PTR_8]], align 8, !sandboxvec [[META45:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.8 = getelementptr i8, ptr %ptr, i64 8
+  store i32 1, ptr %ptr
+  store i32 2, ptr %ptr.4
+  store ptr getelementptr (i32, ptr @arr, i64 2), ptr %ptr.8
+  ret void
+}
+
+; A non-integral pointer cannot be reinterpreted as an integer.
+define void @const_non_integral_ptr(ptr addrspace(5) %ptr) {
+; CHECK-LABEL: define void @const_non_integral_ptr(
+; CHECK-SAME: ptr addrspace(5) [[PTR:%.*]]) {
+; CHECK-NEXT:    [[PTR_8:%.*]] = getelementptr i8, ptr addrspace(5) [[PTR]], i64 8
+; CHECK-NEXT:    store i64 5, ptr addrspace(5) [[PTR]], align 4, !sandboxvec [[META46:![0-9]+]]
+; CHECK-NEXT:    store ptr addrspace(5) null, ptr addrspace(5) [[PTR_8]], align 8, !sandboxvec [[META46]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.8 = getelementptr i8, ptr addrspace(5) %ptr, i64 8
+  store i64 5, ptr addrspace(5) %ptr
+  store ptr addrspace(5) null, ptr addrspace(5) %ptr.8
+  ret void
+}
+
+; The operands are neither all constants nor all loads.
+define void @const_and_loaded_operand(ptr %ptr, ptr %other) {
+; CHECK-LABEL: define void @const_and_loaded_operand(
+; CHECK-SAME: ptr [[PTR:%.*]], ptr [[OTHER:%.*]]) {
+; CHECK-NEXT:    [[PTR_4:%.*]] = getelementptr i8, ptr [[PTR]], i64 4
+; CHECK-NEXT:    [[LD:%.*]] = load float, ptr [[OTHER]], align 4
+; CHECK-NEXT:    store i32 1, ptr [[PTR]], align 4, !sandboxvec [[META47:![0-9]+]]
+; CHECK-NEXT:    store float [[LD]], ptr [[PTR_4]], align 4, !sandboxvec [[META47]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ld = load float, ptr %other
+  store i32 1, ptr %ptr
+  store float %ld, ptr %ptr.4
+  ret void
+}
+
+; The lane type is the narrowest store type, i24 here, and the i32 constant is
+; not a whole number of lanes wide. The i32 store comes first so that the seed
+; slice is still sized in 32-bit steps and reaches the constant packing code.
+define void @const_i32_not_a_multiple_of_the_i24_lane(ptr %ptr) {
+; CHECK-LABEL: define void @const_i32_not_a_multiple_of_the_i24_lane(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:    [[PTR_4:%.*]] = getelementptr i8, ptr [[PTR]], i64 4
+; CHECK-NEXT:    [[PTR_7:%.*]] = getelementptr i8, ptr [[PTR]], i64 7
+; CHECK-NEXT:    [[PTR_10:%.*]] = getelementptr i8, ptr [[PTR]], i64 10
+; CHECK-NEXT:    [[PTR_13:%.*]] = getelementptr i8, ptr [[PTR]], i64 13
+; CHECK-NEXT:    store i32 1, ptr [[PTR]], align 4, !sandboxvec [[META48:![0-9]+]]
+; CHECK-NEXT:    store i24 2, ptr [[PTR_4]], align 4, !sandboxvec [[META48]]
+; CHECK-NEXT:    store i24 3, ptr [[PTR_7]], align 4, !sandboxvec [[META48]]
+; CHECK-NEXT:    store i24 4, ptr [[PTR_10]], align 4, !sandboxvec [[META48]]
+; CHECK-NEXT:    store i24 5, ptr [[PTR_13]], align 4, !sandboxvec [[META48]]
+; CHECK-NEXT:    ret void
+;
+  %ptr.4 = getelementptr i8, ptr %ptr, i64 4
+  %ptr.7 = getelementptr i8, ptr %ptr, i64 7
+  %ptr.10 = getelementptr i8, ptr %ptr, i64 10
+  %ptr.13 = getelementptr i8, ptr %ptr, i64 13
+  store i32 1, ptr %ptr
+  store i24 2, ptr %ptr.4
+  store i24 3, ptr %ptr.7
+  store i24 4, ptr %ptr.10
+  store i24 5, ptr %ptr.13
+  ret void
+}
 ;.
 ; CHECK: [[META0]] = distinct !{!"sandboxregion"}
 ; CHECK: [[META1]] = distinct !{!"sandboxregion"}
@@ -402,4 +856,35 @@ define void @mixed_dboule_pointer_with_four_external_users(ptr %ptr, ptr %ptrExt
 ; CHECK: [[META15]] = distinct !{!"sandboxregion"}
 ; CHECK: [[META16]] = distinct !{!"sandboxregion"}
 ; CHECK: [[META17]] = distinct !{!"sandboxregion"}
+; CHECK: [[META18]] = distinct !{!"sandboxregion"}
+; CHECK: [[META19]] = distinct !{!"sandboxregion"}
+; CHECK: [[META20]] = distinct !{!"sandboxregion"}
+; CHECK: [[META21]] = distinct !{!"sandboxregion"}
+; CHECK: [[META22]] = distinct !{!"sandboxregion"}
+; CHECK: [[META23]] = distinct !{!"sandboxregion"}
+; CHECK: [[META24]] = distinct !{!"sandboxregion"}
+; CHECK: [[META25]] = distinct !{!"sandboxregion"}
+; CHECK: [[META26]] = distinct !{!"sandboxregion"}
+; CHECK: [[META27]] = distinct !{!"sandboxregion"}
+; CHECK: [[META28]] = distinct !{!"sandboxregion"}
+; CHECK: [[META29]] = distinct !{!"sandboxregion"}
+; CHECK: [[META30]] = distinct !{!"sandboxregion"}
+; CHECK: [[META31]] = distinct !{!"sandboxregion"}
+; CHECK: [[META32]] = distinct !{!"sandboxregion"}
+; CHECK: [[META33]] = distinct !{!"sandboxregion"}
+; CHECK: [[META34]] = distinct !{!"sandboxregion"}
+; CHECK: [[META35]] = distinct !{!"sandboxregion"}
+; CHECK: [[META36]] = distinct !{!"sandboxregion"}
+; CHECK: [[META37]] = distinct !{!"sandboxregion"}
+; CHECK: [[META38]] = distinct !{!"sandboxregion"}
+; CHECK: [[META39]] = distinct !{!"sandboxregion"}
+; CHECK: [[META40]] = distinct !{!"sandboxregion"}
+; CHECK: [[META41]] = distinct !{!"sandboxregion"}
+; CHECK: [[META42]] = distinct !{!"sandboxregion"}
+; CHECK: [[META43]] = distinct !{!"sandboxregion"}
+; CHECK: [[META44]] = distinct !{!"sandboxregion"}
+; CHECK: [[META45]] = distinct !{!"sandboxregion"}
+; CHECK: [[META46]] = distinct !{!"sandboxregion"}
+; CHECK: [[META47]] = distinct !{!"sandboxregion"}
+; CHECK: [[META48]] = distinct !{!"sandboxregion"}
 ;.



More information about the llvm-commits mailing list