[llvm] [SROA] Extend SROA to support dynamic indexing (PR #217188)

Stephen Verderame via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 18:46:24 PDT 2026


https://github.com/stephenverderame created https://github.com/llvm/llvm-project/pull/217188

Instead of bailing completely when non-constant GEP indices are encountered, this patch will allow SROA to handle non-constant, but bounded indices by considering an operation as potentially acessing memory between the minimum and maximum address. This would allow splitting aggregates where only part of it is accessed dynamically, which can reduce the size of the alloca.

>From 955363383f25214aa298c9415409025d1c402f06 Mon Sep 17 00:00:00 2001
From: Stephen Verderame <s_verderame at apple.com>
Date: Sun, 9 Aug 2026 12:11:49 -0600
Subject: [PATCH] [SROA] Extend SROA to support dynamic indexing

Instead of bailing completely when non-constant GEP indices are
encountered, this patch will allow SROA to handle non-constant, but
bounded indices by considering an operation as potentially acessing
memory between the minimum and maximum address. This would allow
splitting aggregates where only part of it is accessed dynamically,
which can reduce the size of the alloca.
---
 llvm/include/llvm/Analysis/PtrUseVisitor.h |  33 +-
 llvm/include/llvm/IR/Instructions.h        |  19 +-
 llvm/lib/Analysis/PtrUseVisitor.cpp        |  35 +-
 llvm/lib/IR/Instructions.cpp               |   8 +-
 llvm/lib/Transforms/Scalar/SROA.cpp        | 607 ++++++++++----
 llvm/test/Transforms/SROA/non-constant.ll  | 900 +++++++++++++++++++++
 llvm/test/Transforms/SROA/select-gep.ll    |  14 +-
 7 files changed, 1436 insertions(+), 180 deletions(-)
 create mode 100644 llvm/test/Transforms/SROA/non-constant.ll

diff --git a/llvm/include/llvm/Analysis/PtrUseVisitor.h b/llvm/include/llvm/Analysis/PtrUseVisitor.h
index 304c147190728..50887c7443527 100644
--- a/llvm/include/llvm/Analysis/PtrUseVisitor.h
+++ b/llvm/include/llvm/Analysis/PtrUseVisitor.h
@@ -26,6 +26,7 @@
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/InstVisitor.h"
 #include "llvm/IR/IntrinsicInst.h"
@@ -134,6 +135,7 @@ class PtrUseVisitorBase {
 
     UseAndIsOffsetKnownPair UseAndIsOffsetKnown;
     APInt Offset;
+    APInt HighOffset;
   };
 
   /// The worklist of to-visit uses.
@@ -158,6 +160,9 @@ class PtrUseVisitorBase {
   /// The constant offset of the use if that is known.
   APInt Offset;
 
+  /// The maximum constant offset of the use if that is known.
+  APInt HighOffset;
+
   /// @}
 
   /// Note that the constructor is protected because this class must be a base
@@ -174,7 +179,14 @@ class PtrUseVisitorBase {
   ///
   /// This routine does the heavy lifting of the pointer walk by computing
   /// offsets and looking through GEPs.
-  LLVM_ABI bool adjustOffsetForGEP(GetElementPtrInst &GEPI);
+  ///
+  /// If `RangeAnalysis` is provided, it is queried for the signed range of
+  /// each non-constant index, and the low and high bounds of that range are
+  /// accumulated into `Offset` and `HighOffset` respectively.
+  LLVM_ABI bool adjustOffsetForGEP(
+      GetElementPtrInst &GEPI,
+      function_ref<bool(const Value &, ConstantRange &)> RangeAnalysis =
+          nullptr);
 };
 
 } // end namespace detail
@@ -230,6 +242,7 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
     IntegerType *IntIdxTy = cast<IntegerType>(DL.getIndexType(I.getType()));
     IsOffsetKnown = true;
     Offset = APInt(IntIdxTy->getBitWidth(), 0);
+    HighOffset = Offset;
     PI.reset();
 
     // Enqueue the uses of this pointer.
@@ -240,8 +253,10 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
       UseToVisit ToVisit = Worklist.pop_back_val();
       U = ToVisit.UseAndIsOffsetKnown.getPointer();
       IsOffsetKnown = ToVisit.UseAndIsOffsetKnown.getInt();
-      if (IsOffsetKnown)
+      if (IsOffsetKnown) {
         Offset = std::move(ToVisit.Offset);
+        HighOffset = std::move(ToVisit.HighOffset);
+      }
 
       Instruction *I = cast<Instruction>(U->getUser());
       static_cast<DerivedT*>(this)->visit(I);
@@ -274,9 +289,13 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
       return;
 
     // If we can't walk the GEP, clear the offset.
-    if (!adjustOffsetForGEP(GEPI)) {
+    if (!adjustOffsetForGEP(GEPI, [&](const Value &V, ConstantRange &CR) {
+          return static_cast<DerivedT *>(this)->getNonConstantGepIndexRange(
+              GEPI, V, CR);
+        })) {
       IsOffsetKnown = false;
       Offset = APInt();
+      HighOffset = APInt();
     }
 
     // Enqueue the users now that the offset has been adjusted.
@@ -309,6 +328,14 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
     PI.setEscaped(&CB);
     Base::visitCallBase(CB);
   }
+
+  /// Report the signed range a non-constant GEP index can take, if known.
+  ///
+  /// \returns false if the range cannot be determined.
+  bool getNonConstantGepIndexRange(const GetElementPtrInst &, const Value &,
+                                   ConstantRange &) const {
+    return false;
+  }
 };
 
 } // end namespace llvm
diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h
index e0b26c62d7854..e6a281fee04d2 100644
--- a/llvm/include/llvm/IR/Instructions.h
+++ b/llvm/include/llvm/IR/Instructions.h
@@ -1198,13 +1198,18 @@ class GetElementPtrInst : public Instruction {
   /// Accumulate the constant address offset of this GEP if possible.
   ///
   /// This routine accepts an APInt into which it will accumulate the constant
-  /// offset of this GEP if the GEP is in fact constant. If the GEP is not
-  /// all-constant, it returns false and the value of the offset APInt is
-  /// undefined (it is *not* preserved!). The APInt passed into this routine
-  /// must be at least as wide as the IntPtr type for the address space of
-  /// the base GEP pointer.
-  LLVM_ABI bool accumulateConstantOffset(const DataLayout &DL,
-                                         APInt &Offset) const;
+  /// offset of this GEP. If the GEP is not all-constant and `ExternalAnalysis`
+  /// is null or cannot provide a value for any offset, it returns false and the
+  /// value of the offset APInt is undefined (it is *not* preserved!). The APInt
+  /// passed into this routine must be at least as wide as the IntPtr type for
+  /// the address space of the base GEP pointer. If an ExternalAnalysis is
+  /// supplied, it will be used to determine the value of any non-constant
+  /// indicies. If a value can be provided, `ExternalAnalysis` should return
+  /// true and set the value of the APInt. Otherwise, it should return false,
+  /// which will cause this routine to return false.
+  LLVM_ABI bool accumulateConstantOffset(
+      const DataLayout &DL, APInt &Offset,
+      function_ref<bool(Value &, APInt &)> ExternalAnalysis = nullptr) const;
   LLVM_ABI bool
   collectOffset(const DataLayout &DL, unsigned BitWidth,
                 SmallMapVector<Value *, APInt, 4> &VariableOffsets,
diff --git a/llvm/lib/Analysis/PtrUseVisitor.cpp b/llvm/lib/Analysis/PtrUseVisitor.cpp
index 9c79546f491ef..e8e9b8683655b 100644
--- a/llvm/lib/Analysis/PtrUseVisitor.cpp
+++ b/llvm/lib/Analysis/PtrUseVisitor.cpp
@@ -20,24 +20,43 @@ using namespace llvm;
 void detail::PtrUseVisitorBase::enqueueUsers(Value &I) {
   for (Use &U : I.uses()) {
     if (VisitedUses.insert(&U).second) {
-      UseToVisit NewU = {
-        UseToVisit::UseAndIsOffsetKnownPair(&U, IsOffsetKnown),
-        Offset
-      };
+      UseToVisit NewU = {UseToVisit::UseAndIsOffsetKnownPair(&U, IsOffsetKnown),
+                         Offset, HighOffset};
       Worklist.push_back(std::move(NewU));
     }
   }
 }
 
-bool detail::PtrUseVisitorBase::adjustOffsetForGEP(GetElementPtrInst &GEPI) {
+bool detail::PtrUseVisitorBase::adjustOffsetForGEP(
+    GetElementPtrInst &GEPI,
+    function_ref<bool(const Value &, ConstantRange &)> RangeAnalysis) {
   if (!IsOffsetKnown)
     return false;
 
-  APInt TmpOffset(DL.getIndexTypeSizeInBits(GEPI.getType()), 0);
-  if (GEPI.accumulateConstantOffset(DL, TmpOffset)) {
+  if (!RangeAnalysis) {
+    APInt TmpOffset(DL.getIndexTypeSizeInBits(GEPI.getType()), 0);
+    if (!GEPI.accumulateConstantOffset(DL, TmpOffset))
+      return false;
     Offset += TmpOffset.sextOrTrunc(Offset.getBitWidth());
+    HighOffset += TmpOffset.sextOrTrunc(HighOffset.getBitWidth());
     return true;
   }
 
-  return false;
+  auto AccumulateBound = [&](APInt &Accum, bool IsUpperBound) {
+    auto ExternalAnalysis = [&](Value &V, APInt &Index) {
+      ConstantRange CR(Index.getBitWidth(), /*isFullSet=*/false);
+      if (!RangeAnalysis(V, CR))
+        return false;
+      Index = IsUpperBound ? CR.getSignedMax() : CR.getSignedMin();
+      return true;
+    };
+    APInt Tmp(DL.getIndexTypeSizeInBits(GEPI.getType()), 0);
+    if (!GEPI.accumulateConstantOffset(DL, Tmp, ExternalAnalysis))
+      return false;
+    Accum += Tmp.sextOrTrunc(Accum.getBitWidth());
+    return true;
+  };
+
+  return AccumulateBound(Offset, /*IsUpperBound=*/false) &&
+         AccumulateBound(HighOffset, /*IsUpperBound=*/true);
 }
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 6ca12da2454cc..409fa203abc01 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -1688,10 +1688,12 @@ bool GetElementPtrInst::hasNoUnsignedWrap() const {
   return cast<GEPOperator>(this)->hasNoUnsignedWrap();
 }
 
-bool GetElementPtrInst::accumulateConstantOffset(const DataLayout &DL,
-                                                 APInt &Offset) const {
+bool GetElementPtrInst::accumulateConstantOffset(
+    const DataLayout &DL, APInt &Offset,
+    function_ref<bool(Value &, APInt &)> ExternalAnalysis) const {
   // Delegate to the generic GEPOperator implementation.
-  return cast<GEPOperator>(this)->accumulateConstantOffset(DL, Offset);
+  return cast<GEPOperator>(this)->accumulateConstantOffset(DL, Offset,
+                                                           ExternalAnalysis);
 }
 
 bool GetElementPtrInst::collectOffset(
diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index e4770154e2998..5da912ea346ac 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -48,6 +48,7 @@
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/ConstantFolder.h"
+#include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DataLayout.h"
@@ -333,8 +334,12 @@ static DebugVariable getAggregateVariable(DbgVariableRecord *DVR) {
 /// \param OldAlloca             Alloca for the variable before splitting.
 /// \param IsSplit               True if the store (not necessarily alloca)
 ///                              is being split.
-/// \param OldAllocaOffsetInBits Offset of the slice taken from OldAlloca.
-/// \param SliceSizeInBits       New number of bits being written to.
+/// \param OldAllocaOffsetInBits Offset of the slice taken from OldAlloca, if
+///                              constant. Must not be nullopt if the store is
+///                              being split.
+/// \param SliceSizeInBits       New number of bits being written to, if
+///                              constant. Must not be nullopt if the store is
+///                              being split.
 /// \param OldInst               Instruction that is being split.
 /// \param Inst                  New instruction performing this part of the
 ///                              split store.
@@ -342,10 +347,10 @@ static DebugVariable getAggregateVariable(DbgVariableRecord *DVR) {
 /// \param Value                 Stored value.
 /// \param DL                    Datalayout.
 static void migrateDebugInfo(AllocaInst *OldAlloca, bool IsSplit,
-                             uint64_t OldAllocaOffsetInBits,
-                             uint64_t SliceSizeInBits, Instruction *OldInst,
-                             Instruction *Inst, Value *Dest, Value *Value,
-                             const DataLayout &DL) {
+                             std::optional<uint64_t> OldAllocaOffsetInBits,
+                             std::optional<uint64_t> SliceSizeInBits,
+                             Instruction *OldInst, Instruction *Inst,
+                             Value *Dest, Value *Value, const DataLayout &DL) {
   // If we want allocas to be migrated using this helper then we need to ensure
   // that the BaseFragments map code still works. A simple solution would be
   // to choose to always clone alloca dbg_assigns (rather than sometimes
@@ -391,6 +396,7 @@ static void migrateDebugInfo(AllocaInst *OldAlloca, bool IsSplit,
     bool SetKillLocation = false;
 
     if (IsSplit) {
+      assert(OldAllocaOffsetInBits && SliceSizeInBits);
       std::optional<DIExpression::FragmentInfo> BaseFragment;
       {
         auto R = BaseFragments.find(getAggregateVariable(DbgAssign));
@@ -402,7 +408,7 @@ static void migrateDebugInfo(AllocaInst *OldAlloca, bool IsSplit,
           Expr->getFragmentInfo();
       DIExpression::FragmentInfo NewFragment;
       FragCalcResult Result = calculateFragment(
-          DbgAssign->getVariable(), OldAllocaOffsetInBits, SliceSizeInBits,
+          DbgAssign->getVariable(), *OldAllocaOffsetInBits, *SliceSizeInBits,
           BaseFragment, CurrentFragment, NewFragment);
 
       if (Result == Skip)
@@ -533,12 +539,16 @@ class Slice {
   /// split.
   PointerIntPair<Use *, 1, bool> UseAndIsSplittable;
 
+  /// Whether the slice is indexed by non-constant indices.
+  bool IsDynamic;
+
 public:
   Slice() = default;
 
-  Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable)
+  Slice(uint64_t BeginOffset, uint64_t EndOffset, Use *U, bool IsSplittable,
+        bool IsDynamic)
       : BeginOffset(BeginOffset), EndOffset(EndOffset),
-        UseAndIsSplittable(U, IsSplittable) {}
+        UseAndIsSplittable(U, IsSplittable), IsDynamic(IsDynamic) {}
 
   uint64_t beginOffset() const { return BeginOffset; }
   uint64_t endOffset() const { return EndOffset; }
@@ -551,6 +561,8 @@ class Slice {
   bool isDead() const { return getUse() == nullptr; }
   void kill() { UseAndIsSplittable.setPointer(nullptr); }
 
+  bool isDynamic() const { return IsDynamic; }
+
   /// Support for ordering ranges.
   ///
   /// This provides an ordering over ranges such that start offsets are
@@ -584,6 +596,36 @@ class Slice {
   bool operator!=(const Slice &RHS) const { return !operator==(RHS); }
 };
 
+/// The indices and strides used to offset a pointer from an alloca.
+class AccumulatedGEPIndices {
+  APInt ConstantOffset;
+  SmallVector<std::pair<Value *, APInt>, 4> VariableIndices;
+
+public:
+  explicit AccumulatedGEPIndices(unsigned BitWidth)
+      : ConstantOffset(BitWidth, 0, true) {}
+
+  /// Adds a GEP index of `V` with stride `Stride`.
+  void addIndex(Value *V, const APInt &Stride) {
+    VariableIndices.emplace_back(
+        V, Stride.sextOrTrunc(ConstantOffset.getBitWidth()));
+  }
+
+  /// Gets the constant offset of the pointer relative to `Base`.
+  APInt getAdjustedConstantOffset(uint64_t Base) const {
+    return ConstantOffset - APInt(ConstantOffset.getBitWidth(), Base);
+  }
+
+  /// Increments the constant offset by `V`.
+  void incConstantOffset(const APInt &V) {
+    ConstantOffset += V.sextOrTrunc(ConstantOffset.getBitWidth());
+  }
+
+  auto indices() const {
+    return make_range(VariableIndices.begin(), VariableIndices.end());
+  }
+};
+
 /// Representation of the alloca slices.
 ///
 /// This class represents the slices of an alloca which are formed by its
@@ -594,7 +636,8 @@ class Slice {
 class AllocaSlices {
 public:
   /// Construct the slices of a particular alloca.
-  AllocaSlices(const DataLayout &DL, AllocaInst &AI);
+  AllocaSlices(const DataLayout &DL, AllocaInst &AI, AssumptionCache &AC,
+               DominatorTree &DT);
 
   /// Test whether a pointer to the allocation escapes our analysis.
   ///
@@ -655,6 +698,12 @@ class AllocaSlices {
   /// need to replace with undef.
   ArrayRef<Use *> getDeadOperands() const { return DeadOperands; }
 
+  /// Gets the accumulated offset of `Ptr` from its alloca.
+  const AccumulatedGEPIndices *getAccumulatedGEPIndices(Value *Ptr) const {
+    auto It = PtrOffsets.find(Ptr);
+    return It == PtrOffsets.end() ? nullptr : &It->second;
+  }
+
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
   void print(raw_ostream &OS, const_iterator I, StringRef Indent = "  ") const;
   void printSlice(raw_ostream &OS, const_iterator I,
@@ -694,6 +743,9 @@ class AllocaSlices {
   /// details.
   SmallVector<Slice, 8> Slices;
 
+  /// Symbolic offsets of each pointer value derived from the alloca.
+  SmallDenseMap<Value *, AccumulatedGEPIndices> PtrOffsets;
+
   /// Instructions which will become dead if we rewrite the alloca.
   ///
   /// Note that these are not separated by slice. This is because we expect an
@@ -1023,17 +1075,26 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
 
   const uint64_t AllocSize;
   AllocaSlices &AS;
+  AssumptionCache &AC;
+  DominatorTree &DT;
 
   SmallDenseMap<Instruction *, unsigned> MemTransferSliceMap;
   SmallDenseMap<Instruction *, uint64_t> PHIOrSelectSizes;
 
+  /// Map from GEP instruction and non-constant index to the range of values
+  /// that index can take, if such a range can be determined.
+  SmallDenseMap<std::pair<const Instruction *, const Value *>, ConstantRange>
+      DynGepRanges;
+
   /// Set to de-duplicate dead instructions found in the use walk.
   SmallPtrSet<Instruction *, 4> VisitedDeadInsts;
 
 public:
-  SliceBuilder(const DataLayout &DL, AllocaInst &AI, AllocaSlices &AS)
+  SliceBuilder(const DataLayout &DL, AllocaInst &AI, AllocaSlices &AS,
+               AssumptionCache &AC, DominatorTree &DT)
       : PtrUseVisitor<SliceBuilder>(DL),
-        AllocSize(AI.getAllocationSize(DL)->getFixedValue()), AS(AS) {}
+        AllocSize(AI.getAllocationSize(DL)->getFixedValue()), AS(AS), AC(AC),
+        DT(DT) {}
 
 private:
   void markAsDead(Instruction &I) {
@@ -1041,6 +1102,17 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
       AS.DeadUsers.push_back(&I);
   }
 
+  bool isSliceDynamic() const { return Offset != HighOffset; }
+
+  bool isInvalidDynamicUse() {
+    return isSliceDynamic() && !AS.getAccumulatedGEPIndices(U->get());
+  }
+
+  bool isInvalidDynamicMemIntrinsic(ConstantInt *Length) {
+    return isSliceDynamic() &&
+           (!AS.getAccumulatedGEPIndices(U->get()) || !Length);
+  }
+
   void insertUse(Instruction &I, const APInt &Offset, uint64_t Size,
                  bool IsSplittable = false) {
     // Completely skip uses which have a zero size or start either before or
@@ -1056,7 +1128,7 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     }
 
     uint64_t BeginOffset = Offset.getZExtValue();
-    uint64_t EndOffset = BeginOffset + Size;
+    uint64_t EndOffset = HighOffset.getZExtValue() + Size;
 
     // Clamp the end offset to the end of the allocation. Note that this is
     // formulated to handle even the case where "BeginOffset + Size" overflows.
@@ -1065,7 +1137,8 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     // some instructions are dead but not others. We can't completely ignore
     // them, and so have to record at least the information here.
     assert(AllocSize >= BeginOffset); // Established above.
-    if (Size > AllocSize - BeginOffset) {
+    if (HighOffset.uge(AllocSize) ||
+        Size > AllocSize - HighOffset.getZExtValue()) {
       LLVM_DEBUG(dbgs() << "WARNING: Clamping a " << Size << " byte use @"
                         << Offset << " to remain within the " << AllocSize
                         << " byte alloca:\n"
@@ -1074,13 +1147,18 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
       EndOffset = AllocSize;
     }
 
-    AS.Slices.push_back(Slice(BeginOffset, EndOffset, U, IsSplittable));
+    bool IsDynamic = isSliceDynamic();
+    // We cannot split an alloca that is dynamically indexed.
+    IsSplittable &= !IsDynamic;
+    AS.Slices.push_back(
+        Slice(BeginOffset, EndOffset, U, IsSplittable, IsDynamic));
   }
 
   void visitBitCastInst(BitCastInst &BC) {
     if (BC.use_empty())
       return markAsDead(BC);
 
+    AS.PtrOffsets.try_emplace(&BC, getOrCreateOffsetsOf(BC.getOperand(0)));
     return Base::visitBitCastInst(BC);
   }
 
@@ -1088,6 +1166,8 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     if (ASC.use_empty())
       return markAsDead(ASC);
 
+    AS.PtrOffsets.try_emplace(&ASC,
+                              getOrCreateOffsetsOf(ASC.getPointerOperand()));
     return Base::visitAddrSpaceCastInst(ASC);
   }
 
@@ -1095,6 +1175,7 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     if (GEPI.use_empty())
       return markAsDead(GEPI);
 
+    computeNonConstantGepRanges(GEPI);
     return Base::visitGetElementPtrInst(GEPI);
   }
 
@@ -1106,6 +1187,9 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     bool IsSplittable =
         Ty->isIntegerTy() && !IsVolatile && DL.typeSizeEqualsStoreSize(Ty);
 
+    if (isInvalidDynamicUse())
+      return PI.setAborted(&I);
+
     insertUse(I, Offset, Size, IsSplittable);
   }
 
@@ -1178,7 +1262,7 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
       // Zero-length mem transfer intrinsics can be ignored entirely.
       return markAsDead(II);
 
-    if (!IsOffsetKnown)
+    if (!IsOffsetKnown || isInvalidDynamicMemIntrinsic(Length))
       return PI.setAborted(&II);
 
     insertUse(II, Offset,
@@ -1198,7 +1282,7 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     if (VisitedDeadInsts.count(&II))
       return;
 
-    if (!IsOffsetKnown)
+    if (!IsOffsetKnown || isInvalidDynamicMemIntrinsic(Length))
       return PI.setAborted(&II);
 
     // This side of the transfer is completely out-of-bounds, and so we can
@@ -1351,14 +1435,16 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
     // %other)" may trap because the select may return the first operand
     // "undef".
     if (Value *Result = foldPHINodeOrSelectInst(I)) {
-      if (Result == *U)
+      if (Result == *U) {
         // If the result of the constant fold will be the pointer, recurse
         // through the PHI/select as if we had RAUW'ed it.
         enqueueUsers(I);
-      else
+        AS.PtrOffsets.try_emplace(&I, getOrCreateOffsetsOf(Result));
+      } else {
         // Otherwise the operand to the PHI/select is dead, and we can replace
         // it with poison.
         AS.DeadOperands.push_back(U);
+      }
 
       return;
     }
@@ -1407,15 +1493,58 @@ class AllocaSlices::SliceBuilder : public PtrUseVisitor<SliceBuilder> {
 
     Base::visitCallBase(CB);
   }
+
+  AccumulatedGEPIndices getOrCreateOffsetsOf(Value *Ptr) {
+    // Returns by value so callers can directly re-insert the offsets.
+    return AS.PtrOffsets.try_emplace(Ptr, Offset.getBitWidth()).first->second;
+  }
+
+  void computeNonConstantGepRanges(GetElementPtrInst &GEP) {
+    unsigned IdxBits = DL.getIndexTypeSizeInBits(GEP.getType());
+    SmallMapVector<Value *, APInt, 4> VarOffsets;
+    APInt ConstantOffset(IdxBits, 0);
+    if (!GEP.collectOffset(DL, IdxBits, VarOffsets, ConstantOffset))
+      return;
+
+    auto Offsets = getOrCreateOffsetsOf(GEP.getPointerOperand());
+    Offsets.incConstantOffset(ConstantOffset);
+
+    const SimplifyQuery SQ(DL, /*DT=*/&DT, &AC, /*CxtI=*/&GEP);
+    SmallVector<std::pair<Value *, ConstantRange>, 4> Ranges;
+    for (auto &[V, Stride] : VarOffsets) {
+      ConstantRange CR = computeConstantRange(V, /*ForSigned=*/true, SQ);
+      // Bail if we can't determine the range or if there can be a negative
+      // offset. SROA currently drops negative offset operations as being OOB.
+      if (CR.isFullSet() || CR.isEmptySet() || CR.isSignWrappedSet() ||
+          CR.getSignedMin().isNegative())
+        return;
+      Ranges.emplace_back(V, std::move(CR));
+      Offsets.addIndex(V, Stride);
+    }
+
+    for (auto &[V, CR] : Ranges)
+      DynGepRanges.try_emplace({&GEP, V}, std::move(CR));
+    AS.PtrOffsets.try_emplace(&GEP, Offsets);
+  }
+
+  bool getNonConstantGepIndexRange(const GetElementPtrInst &GEP, const Value &V,
+                                   ConstantRange &CR) const {
+    auto It = DynGepRanges.find({&GEP, &V});
+    if (It == DynGepRanges.end())
+      return false;
+    CR = It->second;
+    return true;
+  }
 };
 
-AllocaSlices::AllocaSlices(const DataLayout &DL, AllocaInst &AI)
+AllocaSlices::AllocaSlices(const DataLayout &DL, AllocaInst &AI,
+                           AssumptionCache &AC, DominatorTree &DT)
     :
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
       AI(AI),
 #endif
       PointerEscapingInstr(nullptr), PointerEscapingInstrReadOnly(nullptr) {
-  SliceBuilder PB(DL, AI, *this);
+  SliceBuilder PB(DL, AI, *this, AC, DT);
   SliceBuilder::PtrInfo PtrI = PB.visitPtr(AI);
   if (PtrI.isEscaped() || PtrI.isAborted()) {
     // FIXME: We should sink the escape vs. abort info into the caller nicely,
@@ -2018,6 +2147,9 @@ static bool isVectorPromotionViableForSlice(Partition &P, const Slice &S,
                                             uint64_t ElementSize,
                                             const DataLayout &DL,
                                             unsigned VScale) {
+  if (S.isDynamic())
+    return false;
+
   // First validate the slice offsets.
   uint64_t BeginOffset =
       std::max(S.beginOffset(), P.beginOffset()) - P.beginOffset();
@@ -2324,6 +2456,9 @@ static bool isIntegerWideningViableForSlice(const Slice &S,
                                             Type *AllocaTy,
                                             const DataLayout &DL,
                                             bool &WholeAllocaOp) {
+  if (S.isDynamic())
+    return false;
+
   uint64_t Size = DL.getTypeStoreSize(AllocaTy).getFixedValue();
 
   uint64_t RelBegin = S.beginOffset() - AllocBeginOffset;
@@ -2668,6 +2803,114 @@ static Value *mergeTwoVectors(Value *V0, Value *V1, const DataLayout &DL,
 
 namespace {
 
+/// Information about a Slice being rewritten.
+class SliceInfo {
+  /// The original offset of the slice currently being rewritten relative to
+  /// the original alloca.
+  uint64_t BeginOffset = 0;
+  uint64_t EndOffset = 0;
+
+  /// The new offsets of the slice currently being rewritten relative to the
+  /// original alloca.
+  uint64_t NewBeginOffset = 0, NewEndOffset = 0;
+
+  uint64_t SliceSize = 0;
+
+  bool IsDynamic = false;
+  bool IsSplittable = false;
+  bool IsSplit = false;
+
+public:
+  SliceInfo() = default;
+  SliceInfo(AllocaSlices::const_iterator I, uint64_t NewAllocaBeginOffset,
+            uint64_t NewAllocaEndOffset) {
+    BeginOffset = I->beginOffset();
+    EndOffset = I->endOffset();
+    IsDynamic = I->isDynamic();
+    IsSplittable = I->isSplittable();
+    IsSplit =
+        BeginOffset < NewAllocaBeginOffset || EndOffset > NewAllocaEndOffset;
+    // A dynamically-indexed slice is unsplittable, so the builder must never
+    // hand us one that spans partitions.
+    assert(!(IsDynamic && IsSplit) &&
+           "Dynamic slice unexpectedly split across partitions!");
+
+    // Compute the intersecting offset range.
+    assert(BeginOffset < NewAllocaEndOffset);
+    assert(EndOffset > NewAllocaBeginOffset);
+    NewBeginOffset = std::max(BeginOffset, NewAllocaBeginOffset);
+    NewEndOffset = std::min(EndOffset, NewAllocaEndOffset);
+
+    SliceSize = NewEndOffset - NewBeginOffset;
+    LLVM_DEBUG(dbgs() << "   Begin:(" << BeginOffset << ", " << EndOffset
+                      << ") NewBegin:(" << NewBeginOffset << ", "
+                      << NewEndOffset << ") NewAllocaBegin:("
+                      << NewAllocaBeginOffset << ", " << NewAllocaEndOffset
+                      << ")\n");
+    assert(IsSplit || NewBeginOffset == BeginOffset);
+  }
+
+  /// Gets the concrete original begin offset of the slice, if it's not dynamic.
+  std::optional<uint64_t> getBeginOffset() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return BeginOffset;
+  }
+  /// Gets the concrete original end offset of the slice, if it's not dynamic.
+  std::optional<uint64_t> getEndOffset() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return EndOffset;
+  }
+  /// Gets the concrete new begin offset of the rewritten slice, if it's not
+  /// dynamic.
+  std::optional<uint64_t> getNewBeginOffset() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return NewBeginOffset;
+  }
+  /// Gets the concrete new end offset of the rewritten slice, if it's not
+  /// dynamic.
+  std::optional<uint64_t> getNewEndOffset() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return NewEndOffset;
+  }
+  /// Gets the concrete new begin offset in bits of the rewritten slice, if it's
+  /// not dynamic.
+  std::optional<uint64_t> getNewBeginOffsetInBits() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return NewBeginOffset * 8;
+  }
+
+  /// Gets the size of the slice, if it's not dynamic.
+  std::optional<uint64_t> getSliceSize() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return SliceSize;
+  }
+  /// Gets the size of the slice in bits, if it's not dynamic.
+  std::optional<uint64_t> getSliceSizeInBits() const {
+    if (IsDynamic)
+      return std::nullopt;
+    return SliceSize * 8;
+  }
+
+  uint64_t getMinBeginOffset() const { return BeginOffset; }
+  uint64_t getMaxEndOffset() const { return EndOffset; }
+  uint64_t getMinNewBeginOffset() const { return NewBeginOffset; }
+  /// Gets the begin offset of the rewritten slice relative to the original
+  /// slice's begin.
+  uint64_t getRelativeBeginOffset() const {
+    return NewBeginOffset - BeginOffset;
+  }
+
+  bool isDynamic() const { return IsDynamic; }
+  bool isSplit() const { return IsSplit; }
+  bool isSplittable() const { return IsSplittable; }
+};
+
 /// Visitor to rewrite instructions using p particular slice of an alloca
 /// to use a new alloca.
 ///
@@ -2706,18 +2949,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
   Type *ElementTy;
   uint64_t ElementSize;
 
-  // The original offset of the slice currently being rewritten relative to
-  // the original alloca.
-  uint64_t BeginOffset = 0;
-  uint64_t EndOffset = 0;
-
-  // The new offsets of the slice currently being rewritten relative to the
-  // original alloca.
-  uint64_t NewBeginOffset = 0, NewEndOffset = 0;
+  SliceInfo CurSlice;
 
-  uint64_t SliceSize = 0;
-  bool IsSplittable = false;
-  bool IsSplit = false;
   Use *OldUse = nullptr;
   Instruction *OldPtr = nullptr;
 
@@ -2771,28 +3004,11 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
   bool visit(AllocaSlices::const_iterator I) {
     bool CanSROA = true;
-    BeginOffset = I->beginOffset();
-    EndOffset = I->endOffset();
-    IsSplittable = I->isSplittable();
-    IsSplit =
-        BeginOffset < NewAllocaBeginOffset || EndOffset > NewAllocaEndOffset;
-    LLVM_DEBUG(dbgs() << "  rewriting " << (IsSplit ? "split " : ""));
+    CurSlice = SliceInfo(I, NewAllocaBeginOffset, NewAllocaEndOffset);
+    LLVM_DEBUG(dbgs() << "  rewriting "
+                      << (CurSlice.isSplit() ? "split " : ""));
     LLVM_DEBUG(AS.printSlice(dbgs(), I, ""));
     LLVM_DEBUG(dbgs() << "\n");
-
-    // Compute the intersecting offset range.
-    assert(BeginOffset < NewAllocaEndOffset);
-    assert(EndOffset > NewAllocaBeginOffset);
-    NewBeginOffset = std::max(BeginOffset, NewAllocaBeginOffset);
-    NewEndOffset = std::min(EndOffset, NewAllocaEndOffset);
-
-    SliceSize = NewEndOffset - NewBeginOffset;
-    LLVM_DEBUG(dbgs() << "   Begin:(" << BeginOffset << ", " << EndOffset
-                      << ") NewBegin:(" << NewBeginOffset << ", "
-                      << NewEndOffset << ") NewAllocaBegin:("
-                      << NewAllocaBeginOffset << ", " << NewAllocaEndOffset
-                      << ")\n");
-    assert(IsSplit || NewBeginOffset == BeginOffset);
     OldUse = I->getUse();
     OldPtr = cast<Instruction>(OldUse->get());
 
@@ -2802,7 +3018,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     // Avoid materializing the name prefix when it is discarded anyway.
     if (!IRB.getContext().shouldDiscardValueNames())
       IRB.getInserter().SetNamePrefix(Twine(NewAI.getName()) + "." +
-                                      Twine(BeginOffset) + ".");
+                                      Twine(CurSlice.getMinBeginOffset()) +
+                                      ".");
 
     CanSROA &= visit(cast<Instruction>(OldUse->getUser()));
     if (VecTy || IntTy)
@@ -2870,6 +3087,11 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     if (P.splitSliceTails().size() > 0)
       return std::nullopt;
 
+    // A dynamic slice does not necessarily use its entire range, so we cannot
+    // optimize this partition.
+    if (any_of(P, [](auto &S) { return S.isDynamic(); }))
+      return std::nullopt;
+
     // Structure to hold store information
     struct StoreInfo {
       StoreInst *Store;
@@ -3262,8 +3484,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
   Value *getNewAllocaSlicePtr(IRBuilderTy &IRB, Type *PointerTy) {
     // Note that the offset computation can use BeginOffset or NewBeginOffset
     // interchangeably for unsplit slices.
-    assert(IsSplit || BeginOffset == NewBeginOffset);
-    uint64_t Offset = NewBeginOffset - NewAllocaBeginOffset;
+    assert(CurSlice.isSplit() || CurSlice.getRelativeBeginOffset() == 0);
 
     StringRef OldName = OldPtr->getName();
     // Skip through the last '.sroa.' component of the name.
@@ -3284,19 +3505,58 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     // Strip any SROA suffixes as well.
     OldName = OldName.substr(0, OldName.find(".sroa_"));
 
+    if (CurSlice.isDynamic())
+      return getDynamicAdjustedPtr(IRB, PointerTy, Twine(OldName) + ".");
+    uint64_t Offset = *CurSlice.getNewBeginOffset() - NewAllocaBeginOffset;
     return getAdjustedPtr(IRB, DL, &NewAI,
                           APInt(DL.getIndexTypeSizeInBits(PointerTy), Offset),
                           PointerTy, Twine(OldName) + ".");
   }
 
+  // If `OldPtr` relies on dynamic GEPs, returns an equivalent of `OldPtr` but
+  // remapped into the new alloca.
+  Value *getDynamicAdjustedPtr(IRBuilderTy &IRB, Type *PointerTy,
+                               const Twine &NamePrefix) {
+    auto *Indices = AS.getAccumulatedGEPIndices(OldPtr);
+    assert(Indices && "Dynamic slice pointer has no recorded indices!");
+    unsigned BitWidth = DL.getIndexTypeSizeInBits(PointerTy);
+    auto ConstantOffset =
+        Indices->getAdjustedConstantOffset(NewAllocaBeginOffset)
+            .sextOrTrunc(BitWidth);
+
+    Type *IdxTy = IRB.getIntNTy(BitWidth);
+    Value *Offset = ConstantInt::get(IdxTy, ConstantOffset);
+    for (auto &[Val, Stride] : Indices->indices()) {
+      Value *Idx = IRB.CreateSExtOrTrunc(Val, IdxTy);
+      Value *Term = IRB.CreateMul(
+          Idx, ConstantInt::get(IdxTy, Stride.sextOrTrunc(BitWidth)),
+          NamePrefix + "sroa_stride");
+      Offset = IRB.CreateAdd(Offset, Term, NamePrefix + "sroa_offset");
+    }
+
+    Value *Ptr =
+        IRB.CreateInBoundsPtrAdd(&NewAI, Offset, NamePrefix + "sroa_idx");
+    return IRB.CreatePointerBitCastOrAddrSpaceCast(Ptr, PointerTy,
+                                                   NamePrefix + "sroa_cast");
+  }
+
   /// Compute suitable alignment to access this slice of the *new*
   /// alloca.
   ///
   /// You can optionally pass a type to this routine and if that type's ABI
   /// alignment is itself suitable, this will return zero.
   Align getSliceAlign() {
-    return commonAlignment(NewAI.getAlign(),
-                           NewBeginOffset - NewAllocaBeginOffset);
+    Align A =
+        commonAlignment(NewAI.getAlign(),
+                        CurSlice.getMinNewBeginOffset() - NewAllocaBeginOffset);
+    if (!CurSlice.isDynamic())
+      return A;
+
+    auto *Indices = AS.getAccumulatedGEPIndices(OldPtr);
+    assert(Indices && "Dynamic slice pointer has no recorded indices!");
+    for (auto &ValStride : Indices->indices())
+      A = commonAlignment(A, ValStride.second.getZExtValue());
+    return A;
   }
 
   unsigned getIndex(uint64_t Offset) {
@@ -3315,8 +3575,9 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
   }
 
   Value *rewriteVectorizedLoadInst(LoadInst &LI) {
-    unsigned BeginIndex = getIndex(NewBeginOffset);
-    unsigned EndIndex = getIndex(NewEndOffset);
+    assert(!CurSlice.isDynamic());
+    unsigned BeginIndex = getIndex(*CurSlice.getNewBeginOffset());
+    unsigned EndIndex = getIndex(*CurSlice.getNewEndOffset());
     assert(EndIndex > BeginIndex && "Empty vector!");
 
     LoadInst *Load =
@@ -3330,6 +3591,10 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
   Value *rewriteIntegerLoad(LoadInst &LI) {
     assert(IntTy && "We cannot insert an integer to the alloca");
     assert(!LI.isVolatile());
+    assert(!CurSlice.isDynamic());
+    uint64_t NewBeginOffset = *CurSlice.getNewBeginOffset();
+    uint64_t NewEndOffset = *CurSlice.getNewEndOffset();
+    uint64_t SliceSize = *CurSlice.getSliceSize();
     Value *V =
         IRB.CreateAlignedLoad(NewAllocaTy, &NewAI, NewAI.getAlign(), "load");
     V = IRB.CreateBitPreservingCastChain(DL, V, IntTy);
@@ -3360,19 +3625,23 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
     unsigned AS = LI.getPointerAddressSpace();
 
-    Type *TargetTy = IsSplit ? Type::getIntNTy(LI.getContext(), SliceSize * 8)
-                             : LI.getType();
+    Type *TargetTy =
+        CurSlice.isSplit()
+            ? Type::getIntNTy(LI.getContext(), *CurSlice.getSliceSizeInBits())
+            : LI.getType();
     bool IsPtrAdjusted = false;
     Value *V;
     if (VecTy) {
       V = rewriteVectorizedLoadInst(LI);
     } else if (IntTy && LI.getType()->isIntegerTy()) {
       V = rewriteIntegerLoad(LI);
-    } else if (NewBeginOffset == NewAllocaBeginOffset &&
-               NewEndOffset == NewAllocaEndOffset &&
+    } else if (!CurSlice.isDynamic() &&
+               *CurSlice.getNewBeginOffset() == NewAllocaBeginOffset &&
+               *CurSlice.getNewEndOffset() == NewAllocaEndOffset &&
                (canConvertValue(DL, NewAllocaTy, TargetTy) ||
                 (NewAllocaTy->isIntegerTy() && TargetTy->isIntegerTy() &&
-                 DL.getTypeStoreSize(TargetTy).getFixedValue() > SliceSize &&
+                 DL.getTypeStoreSize(TargetTy).getFixedValue() >
+                     *CurSlice.getSliceSize() &&
                  !LI.isVolatile()))) {
       Value *NewPtr =
           getPtrToNewAI(LI.getPointerAddressSpace(), LI.isVolatile());
@@ -3391,7 +3660,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       // Do this after copyMetadataForLoad() to preserve the TBAA shift.
       if (AATags)
         NewLI->setAAMetadata(AATags.adjustForAccess(
-            NewBeginOffset - BeginOffset, NewLI->getType(), DL));
+            CurSlice.getRelativeBeginOffset(), NewLI->getType(), DL));
 
       // Try to preserve nonnull metadata
       V = NewLI;
@@ -3415,7 +3684,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
       if (AATags)
         NewLI->setAAMetadata(AATags.adjustForAccess(
-            NewBeginOffset - BeginOffset, NewLI->getType(), DL));
+            CurSlice.getRelativeBeginOffset(), NewLI->getType(), DL));
 
       if (LI.isVolatile())
         NewLI->setAtomic(LI.getOrdering(), LI.getSyncScopeID());
@@ -3427,11 +3696,12 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     }
     V = IRB.CreateBitPreservingCastChain(DL, V, TargetTy);
 
-    if (IsSplit) {
+    if (CurSlice.isSplit()) {
       assert(!LI.isVolatile());
       assert(LI.getType()->isIntegerTy() &&
              "Only integer type loads and stores are split");
-      assert(SliceSize < DL.getTypeStoreSize(LI.getType()).getFixedValue() &&
+      assert(*CurSlice.getSliceSize() <
+                 DL.getTypeStoreSize(LI.getType()).getFixedValue() &&
              "Split load isn't smaller than original load");
       assert(DL.typeSizeEqualsStoreSize(LI.getType()) &&
              "Non-byte-multiple bit width");
@@ -3449,8 +3719,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       Value *Placeholder =
           new LoadInst(LI.getType(), PoisonValue::get(IRB.getPtrTy(AS)), "",
                        false, Align(1));
-      V = insertInteger(DL, IRB, Placeholder, V, NewBeginOffset - BeginOffset,
-                        "insert");
+      V = insertInteger(DL, IRB, Placeholder, V,
+                        CurSlice.getRelativeBeginOffset(), "insert");
       LI.replaceAllUsesWith(V);
       Placeholder->replaceAllUsesWith(&LI);
       Placeholder->deleteValue();
@@ -3466,12 +3736,13 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
   bool rewriteVectorizedStoreInst(Value *V, StoreInst &SI, Value *OldOp,
                                   AAMDNodes AATags) {
+    assert(!CurSlice.isDynamic());
     // Capture V for the purpose of debug-info accounting once it's converted
     // to a vector store.
     Value *OrigV = V;
     if (V->getType() != VecTy) {
-      unsigned BeginIndex = getIndex(NewBeginOffset);
-      unsigned EndIndex = getIndex(NewEndOffset);
+      unsigned BeginIndex = getIndex(*CurSlice.getNewBeginOffset());
+      unsigned EndIndex = getIndex(*CurSlice.getNewEndOffset());
       assert(EndIndex > BeginIndex && "Empty vector!");
       unsigned NumElements = EndIndex - BeginIndex;
       assert(NumElements <= cast<FixedVectorType>(VecTy)->getNumElements() &&
@@ -3491,13 +3762,15 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     Store->copyMetadata(SI, {LLVMContext::MD_mem_parallel_loop_access,
                              LLVMContext::MD_access_group});
     if (AATags)
-      Store->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                  V->getType(), DL));
+      Store->setAAMetadata(AATags.adjustForAccess(
+          CurSlice.getRelativeBeginOffset(), V->getType(), DL));
     Pass.DeadInsts.push_back(&SI);
 
     // NOTE: Careful to use OrigV rather than V.
-    migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &SI,
-                     Store, Store->getPointerOperand(), OrigV, DL);
+    migrateDebugInfo(&OldAI, CurSlice.isSplit(),
+                     CurSlice.getNewBeginOffsetInBits(),
+                     CurSlice.getSliceSizeInBits(), &SI, Store,
+                     Store->getPointerOperand(), OrigV, DL);
     LLVM_DEBUG(dbgs() << "          to: " << *Store << "\n");
     return true;
   }
@@ -3505,13 +3778,15 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
   bool rewriteIntegerStore(Value *V, StoreInst &SI, AAMDNodes AATags) {
     assert(IntTy && "We cannot extract an integer from the alloca");
     assert(!SI.isVolatile());
+    assert(!CurSlice.isDynamic());
     if (DL.getTypeSizeInBits(V->getType()).getFixedValue() !=
         IntTy->getBitWidth()) {
       Value *Old = IRB.CreateAlignedLoad(NewAllocaTy, &NewAI, NewAI.getAlign(),
                                          "oldload");
       Old = IRB.CreateBitPreservingCastChain(DL, Old, IntTy);
-      assert(BeginOffset >= NewAllocaBeginOffset && "Out of bounds offset");
-      uint64_t Offset = BeginOffset - NewAllocaBeginOffset;
+      assert(*CurSlice.getBeginOffset() >= NewAllocaBeginOffset &&
+             "Out of bounds offset");
+      uint64_t Offset = *CurSlice.getBeginOffset() - NewAllocaBeginOffset;
       V = insertInteger(DL, IRB, Old, SI.getValueOperand(), Offset, "insert");
     }
     V = IRB.CreateBitPreservingCastChain(DL, V, NewAllocaTy);
@@ -3519,12 +3794,13 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     Store->copyMetadata(SI, {LLVMContext::MD_mem_parallel_loop_access,
                              LLVMContext::MD_access_group});
     if (AATags)
-      Store->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                  V->getType(), DL));
+      Store->setAAMetadata(AATags.adjustForAccess(
+          CurSlice.getRelativeBeginOffset(), V->getType(), DL));
 
-    migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &SI,
-                     Store, Store->getPointerOperand(),
-                     Store->getValueOperand(), DL);
+    migrateDebugInfo(&OldAI, CurSlice.isSplit(),
+                     CurSlice.getNewBeginOffsetInBits(),
+                     CurSlice.getSliceSizeInBits(), &SI, Store,
+                     Store->getPointerOperand(), Store->getValueOperand(), DL);
 
     Pass.DeadInsts.push_back(&SI);
     LLVM_DEBUG(dbgs() << "          to: " << *Store << "\n");
@@ -3546,15 +3822,17 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
         Pass.PostPromotionWorklist.insert(AI);
 
     TypeSize StoreSize = DL.getTypeStoreSize(V->getType());
-    if (StoreSize.isFixed() && SliceSize < StoreSize.getFixedValue()) {
+    if (!CurSlice.isDynamic() && StoreSize.isFixed() &&
+        *CurSlice.getSliceSize() < StoreSize.getFixedValue()) {
       assert(!SI.isVolatile());
       assert(V->getType()->isIntegerTy() &&
              "Only integer type loads and stores are split");
       assert(DL.typeSizeEqualsStoreSize(V->getType()) &&
              "Non-byte-multiple bit width");
-      IntegerType *NarrowTy = Type::getIntNTy(SI.getContext(), SliceSize * 8);
-      V = extractInteger(DL, IRB, V, NarrowTy, NewBeginOffset - BeginOffset,
-                         "extract");
+      IntegerType *NarrowTy =
+          Type::getIntNTy(SI.getContext(), *CurSlice.getSliceSizeInBits());
+      V = extractInteger(DL, IRB, V, NarrowTy,
+                         CurSlice.getRelativeBeginOffset(), "extract");
     }
 
     if (VecTy)
@@ -3563,8 +3841,9 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       return rewriteIntegerStore(V, SI, AATags);
 
     StoreInst *NewSI;
-    if (NewBeginOffset == NewAllocaBeginOffset &&
-        NewEndOffset == NewAllocaEndOffset &&
+    if (!CurSlice.isDynamic() &&
+        *CurSlice.getNewBeginOffset() == NewAllocaBeginOffset &&
+        *CurSlice.getNewEndOffset() == NewAllocaEndOffset &&
         canConvertValue(DL, V->getType(), NewAllocaTy)) {
       V = IRB.CreateBitPreservingCastChain(DL, V, NewAllocaTy);
       Value *NewPtr =
@@ -3581,16 +3860,17 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     NewSI->copyMetadata(SI, {LLVMContext::MD_mem_parallel_loop_access,
                              LLVMContext::MD_access_group});
     if (AATags)
-      NewSI->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                  V->getType(), DL));
+      NewSI->setAAMetadata(AATags.adjustForAccess(
+          CurSlice.getRelativeBeginOffset(), V->getType(), DL));
     if (SI.isVolatile())
       NewSI->setAtomic(SI.getOrdering(), SI.getSyncScopeID());
     if (NewSI->isAtomic())
       NewSI->setAlignment(SI.getAlign());
 
-    migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &SI,
-                     NewSI, NewSI->getPointerOperand(),
-                     NewSI->getValueOperand(), DL);
+    migrateDebugInfo(&OldAI, CurSlice.isSplit(),
+                     CurSlice.getNewBeginOffsetInBits(),
+                     CurSlice.getSliceSizeInBits(), &SI, NewSI,
+                     NewSI->getPointerOperand(), NewSI->getValueOperand(), DL);
 
     Pass.DeadInsts.push_back(&SI);
     deleteIfTriviallyDead(OldOp);
@@ -3643,8 +3923,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     // If the memset has a variable size, it cannot be split, just adjust the
     // pointer to the new alloca.
     if (!isa<ConstantInt>(II.getLength())) {
-      assert(!IsSplit);
-      assert(NewBeginOffset == BeginOffset);
+      assert(!CurSlice.isSplit() && !CurSlice.isDynamic());
+      assert(CurSlice.getRelativeBeginOffset() == 0);
       II.setDest(getNewAllocaSlicePtr(IRB, OldPtr->getType()));
       II.setDestAlignment(getSliceAlign());
       // In theory we should call migrateDebugInfo here. However, we do not
@@ -3664,7 +3944,11 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     const bool CanContinue = [&]() {
       if (VecTy || IntTy)
         return true;
-      if (BeginOffset > NewAllocaBeginOffset || EndOffset < NewAllocaEndOffset)
+      if (CurSlice.isDynamic())
+        // A dynamic slice must retain the actual memset.
+        return false;
+      if (*CurSlice.getBeginOffset() > NewAllocaBeginOffset ||
+          *CurSlice.getEndOffset() < NewAllocaEndOffset)
         return false;
       // Length must be in range for FixedVectorType.
       auto *C = cast<ConstantInt>(II.getLength());
@@ -3681,21 +3965,26 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     // a single value type, just emit a memset.
     if (!CanContinue) {
       Type *SizeTy = II.getLength()->getType();
-      unsigned Sz = NewEndOffset - NewBeginOffset;
+      unsigned Sz = CurSlice.isDynamic()
+                        ? cast<ConstantInt>(II.getLength())->getZExtValue()
+                        : *CurSlice.getSliceSize();
       Constant *Size = ConstantInt::get(SizeTy, Sz);
       MemIntrinsic *New = cast<MemIntrinsic>(IRB.CreateMemSet(
           getNewAllocaSlicePtr(IRB, OldPtr->getType()), II.getValue(), Size,
           MaybeAlign(getSliceAlign()), II.isVolatile()));
       if (AATags)
         New->setAAMetadata(
-            AATags.adjustForAccess(NewBeginOffset - BeginOffset, Sz));
+            AATags.adjustForAccess(CurSlice.getRelativeBeginOffset(), Sz));
 
-      migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &II,
-                       New, New->getRawDest(), nullptr, DL);
+      migrateDebugInfo(&OldAI, CurSlice.isSplit(),
+                       CurSlice.getNewBeginOffsetInBits(),
+                       CurSlice.getSliceSizeInBits(), &II, New,
+                       New->getRawDest(), nullptr, DL);
 
       LLVM_DEBUG(dbgs() << "          to: " << *New << "\n");
       return false;
     }
+    assert(!CurSlice.isDynamic());
 
     // If we can represent this as a simple value, we have to build the actual
     // value to store, which requires expanding the byte present in memset to
@@ -3708,8 +3997,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       // If this is a memset of a vectorized alloca, insert it.
       assert(ElementTy == ScalarTy);
 
-      unsigned BeginIndex = getIndex(NewBeginOffset);
-      unsigned EndIndex = getIndex(NewEndOffset);
+      unsigned BeginIndex = getIndex(*CurSlice.getNewBeginOffset());
+      unsigned EndIndex = getIndex(*CurSlice.getNewEndOffset());
       assert(EndIndex > BeginIndex && "Empty vector!");
       unsigned NumElements = EndIndex - BeginIndex;
       assert(NumElements <= cast<FixedVectorType>(VecTy)->getNumElements() &&
@@ -3728,16 +4017,15 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       // If this is a memset on an alloca where we can widen stores, insert the
       // set integer.
       assert(!II.isVolatile());
-
-      uint64_t Size = NewEndOffset - NewBeginOffset;
+      uint64_t Size = *CurSlice.getSliceSize();
       V = getIntegerSplat(II.getValue(), Size);
 
-      if (IntTy && (NewBeginOffset != NewAllocaBeginOffset ||
-                    NewEndOffset != NewAllocaEndOffset)) {
+      if (IntTy && (*CurSlice.getNewBeginOffset() != NewAllocaBeginOffset ||
+                    *CurSlice.getNewEndOffset() != NewAllocaEndOffset)) {
         Value *Old = IRB.CreateAlignedLoad(NewAllocaTy, &NewAI,
                                            NewAI.getAlign(), "oldload");
         Old = IRB.CreateBitPreservingCastChain(DL, Old, IntTy);
-        uint64_t Offset = NewBeginOffset - NewAllocaBeginOffset;
+        uint64_t Offset = *CurSlice.getNewBeginOffset() - NewAllocaBeginOffset;
         V = insertInteger(DL, IRB, Old, V, Offset, "insert");
       } else {
         assert(V->getType() == IntTy &&
@@ -3746,8 +4034,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       V = IRB.CreateBitPreservingCastChain(DL, V, NewAllocaTy);
     } else {
       // Established these invariants above.
-      assert(NewBeginOffset == NewAllocaBeginOffset);
-      assert(NewEndOffset == NewAllocaEndOffset);
+      assert(*CurSlice.getNewBeginOffset() == NewAllocaBeginOffset);
+      assert(*CurSlice.getNewEndOffset() == NewAllocaEndOffset);
 
       V = getIntegerSplat(II.getValue(),
                           DL.getTypeSizeInBits(ScalarTy).getFixedValue() / 8);
@@ -3764,11 +4052,13 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     New->copyMetadata(II, {LLVMContext::MD_mem_parallel_loop_access,
                            LLVMContext::MD_access_group});
     if (AATags)
-      New->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                V->getType(), DL));
+      New->setAAMetadata(AATags.adjustForAccess(
+          CurSlice.getRelativeBeginOffset(), V->getType(), DL));
 
-    migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &II,
-                     New, New->getPointerOperand(), V, DL);
+    migrateDebugInfo(&OldAI, CurSlice.isSplit(),
+                     CurSlice.getNewBeginOffsetInBits(),
+                     CurSlice.getSliceSizeInBits(), &II, New,
+                     New->getPointerOperand(), V, DL);
 
     LLVM_DEBUG(dbgs() << "          to: " << *New << "\n");
     return !II.isVolatile();
@@ -3794,7 +4084,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     // a variable length. We may also be dealing with memmove instead of
     // memcpy, and so simply updating the pointers is the necessary for us to
     // update both source and dest of a single call.
-    if (!IsSplittable) {
+    if (!CurSlice.isSplittable()) {
       Value *AdjustedPtr = getNewAllocaSlicePtr(IRB, OldPtr->getType());
       if (IsDest) {
         // Update the address component of linked dbg.assigns.
@@ -3814,6 +4104,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       deleteIfTriviallyDead(OldPtr);
       return false;
     }
+    assert(!CurSlice.isDynamic());
     // For split transfer intrinsics we have an incredibly useful assurance:
     // the source and destination do not reside within the same alloca, and at
     // least one of them does not escape. This means that we can replace
@@ -3822,23 +4113,24 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
     // If this doesn't map cleanly onto the alloca type, and that type isn't
     // a single value type, just emit a memcpy.
-    bool EmitMemCpy =
-        !VecTy && !IntTy &&
-        (BeginOffset > NewAllocaBeginOffset || EndOffset < NewAllocaEndOffset ||
-         SliceSize != DL.getTypeStoreSize(NewAllocaTy).getFixedValue() ||
-         !DL.typeSizeEqualsStoreSize(NewAllocaTy) ||
-         !NewAllocaTy->isSingleValueType());
+    bool EmitMemCpy = !VecTy && !IntTy &&
+                      (*CurSlice.getBeginOffset() > NewAllocaBeginOffset ||
+                       *CurSlice.getEndOffset() < NewAllocaEndOffset ||
+                       *CurSlice.getSliceSize() !=
+                           DL.getTypeStoreSize(NewAllocaTy).getFixedValue() ||
+                       !DL.typeSizeEqualsStoreSize(NewAllocaTy) ||
+                       !NewAllocaTy->isSingleValueType());
 
     // If we're just going to emit a memcpy, the alloca hasn't changed, and the
     // size hasn't been shrunk based on analysis of the viable range, this is
     // a no-op.
     if (EmitMemCpy && &OldAI == &NewAI) {
       // Ensure the start lines up.
-      assert(NewBeginOffset == BeginOffset);
+      assert(CurSlice.getRelativeBeginOffset() == 0);
 
       // Rewrite the size as needed.
-      if (NewEndOffset != EndOffset)
-        II.setLength(NewEndOffset - NewBeginOffset);
+      if (*CurSlice.getNewEndOffset() != *CurSlice.getEndOffset())
+        II.setLength(*CurSlice.getSliceSize());
       return false;
     }
     // Record this instruction for deletion.
@@ -3859,7 +4151,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
     // Compute the relative offset for the other pointer within the transfer.
     unsigned OffsetWidth = DL.getIndexSizeInBits(OtherAS);
-    APInt OtherOffset(OffsetWidth, NewBeginOffset - BeginOffset);
+    APInt OtherOffset(OffsetWidth, CurSlice.getRelativeBeginOffset());
     Align OtherAlign =
         (IsDest ? II.getSourceAlign() : II.getDestAlign()).valueOrOne();
     OtherAlign =
@@ -3873,7 +4165,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
       Value *OurPtr = getNewAllocaSlicePtr(IRB, OldPtr->getType());
       Type *SizeTy = II.getLength()->getType();
-      Constant *Size = ConstantInt::get(SizeTy, NewEndOffset - NewBeginOffset);
+      Constant *Size = ConstantInt::get(SizeTy, *CurSlice.getSliceSize());
 
       Value *DestPtr, *SrcPtr;
       MaybeAlign DestAlign, SrcAlign;
@@ -3892,27 +4184,30 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       CallInst *New = IRB.CreateMemCpy(DestPtr, DestAlign, SrcPtr, SrcAlign,
                                        Size, II.isVolatile());
       if (AATags)
-        New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset));
+        New->setAAMetadata(AATags.shift(CurSlice.getRelativeBeginOffset()));
 
       APInt Offset(DL.getIndexTypeSizeInBits(DestPtr->getType()), 0);
       if (IsDest) {
-        migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8,
-                         &II, New, DestPtr, nullptr, DL);
+        migrateDebugInfo(
+            &OldAI, CurSlice.isSplit(), CurSlice.getNewBeginOffsetInBits(),
+            CurSlice.getSliceSizeInBits(), &II, New, DestPtr, nullptr, DL);
       } else if (AllocaInst *Base = dyn_cast<AllocaInst>(
                      DestPtr->stripAndAccumulateConstantOffsets(
                          DL, Offset, /*AllowNonInbounds*/ true))) {
-        migrateDebugInfo(Base, IsSplit, Offset.getZExtValue() * 8,
-                         SliceSize * 8, &II, New, DestPtr, nullptr, DL);
+        migrateDebugInfo(Base, CurSlice.isSplit(), Offset.getZExtValue() * 8,
+                         CurSlice.getSliceSizeInBits(), &II, New, DestPtr,
+                         nullptr, DL);
       }
       LLVM_DEBUG(dbgs() << "          to: " << *New << "\n");
       return false;
     }
 
-    bool IsWholeAlloca = NewBeginOffset == NewAllocaBeginOffset &&
-                         NewEndOffset == NewAllocaEndOffset;
-    uint64_t Size = NewEndOffset - NewBeginOffset;
-    unsigned BeginIndex = VecTy ? getIndex(NewBeginOffset) : 0;
-    unsigned EndIndex = VecTy ? getIndex(NewEndOffset) : 0;
+    bool IsWholeAlloca =
+        *CurSlice.getNewBeginOffset() == NewAllocaBeginOffset &&
+        *CurSlice.getNewEndOffset() == NewAllocaEndOffset;
+    uint64_t Size = *CurSlice.getSliceSize();
+    unsigned BeginIndex = VecTy ? getIndex(*CurSlice.getNewBeginOffset()) : 0;
+    unsigned EndIndex = VecTy ? getIndex(*CurSlice.getNewEndOffset()) : 0;
     unsigned NumElements = EndIndex - BeginIndex;
     IntegerType *SubIntTy =
         IntTy ? Type::getIntNTy(IntTy->getContext(), Size * 8) : nullptr;
@@ -3958,7 +4253,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       Src =
           IRB.CreateAlignedLoad(NewAllocaTy, &NewAI, NewAI.getAlign(), "load");
       Src = IRB.CreateBitPreservingCastChain(DL, Src, IntTy);
-      uint64_t Offset = NewBeginOffset - NewAllocaBeginOffset;
+      uint64_t Offset = *CurSlice.getNewBeginOffset() - NewAllocaBeginOffset;
       Src = extractInteger(DL, IRB, Src, SubIntTy, Offset, "extract");
     } else {
       LoadInst *Load = IRB.CreateAlignedLoad(OtherTy, SrcPtr, SrcAlign,
@@ -3966,8 +4261,8 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       Load->copyMetadata(II, {LLVMContext::MD_mem_parallel_loop_access,
                               LLVMContext::MD_access_group});
       if (AATags)
-        Load->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                   Load->getType(), DL));
+        Load->setAAMetadata(AATags.adjustForAccess(
+            CurSlice.getRelativeBeginOffset(), Load->getType(), DL));
       Src = Load;
     }
 
@@ -3979,7 +4274,7 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
       Value *Old = IRB.CreateAlignedLoad(NewAllocaTy, &NewAI, NewAI.getAlign(),
                                          "oldload");
       Old = IRB.CreateBitPreservingCastChain(DL, Old, IntTy);
-      uint64_t Offset = NewBeginOffset - NewAllocaBeginOffset;
+      uint64_t Offset = *CurSlice.getNewBeginOffset() - NewAllocaBeginOffset;
       Src = insertInteger(DL, IRB, Old, Src, Offset, "insert");
       Src = IRB.CreateBitPreservingCastChain(DL, Src, NewAllocaTy);
     }
@@ -3989,19 +4284,21 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     Store->copyMetadata(II, {LLVMContext::MD_mem_parallel_loop_access,
                              LLVMContext::MD_access_group});
     if (AATags)
-      Store->setAAMetadata(AATags.adjustForAccess(NewBeginOffset - BeginOffset,
-                                                  Src->getType(), DL));
+      Store->setAAMetadata(AATags.adjustForAccess(
+          CurSlice.getRelativeBeginOffset(), Src->getType(), DL));
 
     APInt Offset(DL.getIndexTypeSizeInBits(DstPtr->getType()), 0);
     if (IsDest) {
 
-      migrateDebugInfo(&OldAI, IsSplit, NewBeginOffset * 8, SliceSize * 8, &II,
-                       Store, DstPtr, Src, DL);
+      migrateDebugInfo(
+          &OldAI, CurSlice.isSplit(), CurSlice.getNewBeginOffsetInBits(),
+          CurSlice.getSliceSizeInBits(), &II, Store, DstPtr, Src, DL);
     } else if (AllocaInst *Base = dyn_cast<AllocaInst>(
                    DstPtr->stripAndAccumulateConstantOffsets(
                        DL, Offset, /*AllowNonInbounds*/ true))) {
-      migrateDebugInfo(Base, IsSplit, Offset.getZExtValue() * 8, SliceSize * 8,
-                       &II, Store, DstPtr, Src, DL);
+      migrateDebugInfo(Base, CurSlice.isSplit(), Offset.getZExtValue() * 8,
+                       CurSlice.getSliceSizeInBits(), &II, Store, DstPtr, Src,
+                       DL);
     }
 
     LLVM_DEBUG(dbgs() << "          to: " << *Store << "\n");
@@ -4069,8 +4366,10 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
 
   bool visitPHINode(PHINode &PN) {
     LLVM_DEBUG(dbgs() << "    original: " << PN << "\n");
-    assert(BeginOffset >= NewAllocaBeginOffset && "PHIs are unsplittable");
-    assert(EndOffset <= NewAllocaEndOffset && "PHIs are unsplittable");
+    assert(CurSlice.getMinBeginOffset() >= NewAllocaBeginOffset &&
+           "PHIs are unsplittable");
+    assert(CurSlice.getMaxEndOffset() <= NewAllocaEndOffset &&
+           "PHIs are unsplittable");
 
     // We would like to compute a new pointer in only one place, but have it be
     // as local as possible to the PHI. To do that, we re-use the location of
@@ -4105,8 +4404,10 @@ class AllocaSliceRewriter : public InstVisitor<AllocaSliceRewriter, bool> {
     LLVM_DEBUG(dbgs() << "    original: " << SI << "\n");
     assert((SI.getTrueValue() == OldPtr || SI.getFalseValue() == OldPtr) &&
            "Pointer isn't an operand!");
-    assert(BeginOffset >= NewAllocaBeginOffset && "Selects are unsplittable");
-    assert(EndOffset <= NewAllocaEndOffset && "Selects are unsplittable");
+    assert(CurSlice.getMinBeginOffset() >= NewAllocaBeginOffset &&
+           "Selects are unsplittable");
+    assert(CurSlice.getMaxEndOffset() <= NewAllocaEndOffset &&
+           "Selects are unsplittable");
 
     Value *NewPtr = getNewAllocaSlicePtr(IRB, OldPtr->getType());
     // Replace the operands which were using the old pointer.
@@ -5070,7 +5371,7 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
       NewSlices.push_back(
           Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
                 &PLoad->getOperandUse(PLoad->getPointerOperandIndex()),
-                /*IsSplittable*/ false));
+                /*IsSplittable*/ false, /*IsDynamic*/ false));
       LLVM_DEBUG(dbgs() << "    new slice [" << NewSlices.back().beginOffset()
                         << ", " << NewSlices.back().endOffset()
                         << "): " << *PLoad << "\n");
@@ -5229,7 +5530,7 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
       NewSlices.push_back(
           Slice(BaseOffset + PartOffset, BaseOffset + PartOffset + PartSize,
                 &PStore->getOperandUse(PStore->getPointerOperandIndex()),
-                /*IsSplittable*/ false));
+                /*IsSplittable*/ false, /*IsDynamic*/ false));
       LLVM_DEBUG(dbgs() << "    new slice [" << NewSlices.back().beginOffset()
                         << ", " << NewSlices.back().endOffset()
                         << "): " << *PStore << "\n");
@@ -6162,7 +6463,7 @@ SROA::runOnAlloca(AllocaInst &AI) {
   Changed |= AggRewriter.rewrite(AI);
 
   // Build the slices using a recursive instruction-visiting builder.
-  AllocaSlices AS(DL, AI);
+  AllocaSlices AS(DL, AI, *AC, DTU->getDomTree());
   LLVM_DEBUG(AS.print(dbgs()));
   if (AS.isEscaped())
     return {Changed, CFGChanged};
diff --git a/llvm/test/Transforms/SROA/non-constant.ll b/llvm/test/Transforms/SROA/non-constant.ll
new file mode 100644
index 0000000000000..a9b5e633c66d5
--- /dev/null
+++ b/llvm/test/Transforms/SROA/non-constant.ll
@@ -0,0 +1,900 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=sroa -S < %s | FileCheck %s
+
+target datalayout = "e-p:64:64-p1:64:64-p7:32:32-i64:64-i128:128-n8:16:32:64-S128"
+
+%struct.ArrHolder = type { i32, [8 x i32], i32 }
+%struct.TwoD = type { i32, [4 x [4 x i32]], i32 }
+%struct.TwoArrays = type { [10 x i32], [20 x i32] }
+
+declare void @llvm.assume(i1)
+declare void @llvm.lifetime.start.p0(ptr nocapture)
+declare void @llvm.lifetime.end.p0(ptr nocapture)
+declare void @llvm.memset.p0.i64(ptr nocapture, i8, i64, i1)
+declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1)
+
+define i32 @split_dyn_array_const_scalar(i64 %i, i32 %v) {
+; CHECK-LABEL: @split_dyn_array_const_scalar(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_ARR_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_ARR_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_ARR_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_STRIDE1:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_OFFSET2:%.*]] = add i64 0, [[A_SROA_2_4_ARR_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_ARR_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_SROA_2_4_LA:%.*]] = load i32, ptr [[A_SROA_2_4_ARR_SROA_IDX3]], align 4
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[A_SROA_2_4_LA]], 42
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %ld_arr = load i32, ptr %arr, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %b = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  %ld_b = load i32, ptr %b, align 4
+  %sum = add i32 %ld_arr, %ld_b
+  ret i32 %sum
+}
+
+define i32 @split_two_dyn_indices(i64 %i, i64 %j, i32 %v) {
+; CHECK-LABEL: @split_two_dyn_indices(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [4 x [4 x i32]], align 4
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_SROA_2_4_P_SROA_STRIDE:%.*]] = mul i64 [[I]], 16
+; CHECK-NEXT:    [[A_SROA_2_4_P_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_P_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_2_4_P_SROA_STRIDE1:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_P_SROA_OFFSET2:%.*]] = add i64 [[A_SROA_2_4_P_SROA_OFFSET]], [[A_SROA_2_4_P_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_0_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_P_SROA_OFFSET2]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_P_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 7
+;
+entry:
+  %a = alloca %struct.TwoD, align 4
+  %ci = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 4
+  call void @llvm.assume(i1 %cj)
+  %p = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 1, i64 %i, i64 %j
+  store i32 %v, ptr %p, align 4
+  %sc = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 0
+  store i32 7, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_gep_chain(i64 %i, i64 %j, i32 %v) {
+; CHECK-LABEL: @split_dyn_gep_chain(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [52 x i8], align 4
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_SROA_0_20_ELT_SROA_STRIDE:%.*]] = mul i64 [[I]], 16
+; CHECK-NEXT:    [[A_SROA_0_20_ELT_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_20_ELT_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_20_ELT_SROA_STRIDE1:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_SROA_0_20_ELT_SROA_OFFSET2:%.*]] = add i64 [[A_SROA_0_20_ELT_SROA_OFFSET]], [[A_SROA_0_20_ELT_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_0_0_ELT_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_20_ELT_SROA_OFFSET2]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_ELT_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_SROA_0_68_SC_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 48
+; CHECK-NEXT:    store i32 7, ptr [[A_SROA_0_68_SC_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_SROA_0_68_SC_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 48
+; CHECK-NEXT:    [[A_SROA_0_68_LS:%.*]] = load i32, ptr [[A_SROA_0_68_SC_SROA_IDX3]], align 4
+; CHECK-NEXT:    ret i32 [[A_SROA_0_68_LS]]
+;
+entry:
+  %a = alloca %struct.TwoD, align 4
+  %ci = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 4
+  call void @llvm.assume(i1 %cj)
+  %row = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 1, i64 %i
+  %elt = getelementptr inbounds [4 x i32], ptr %row, i64 1, i64 %j
+  store i32 %v, ptr %elt, align 4
+  %sc = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 2
+  store i32 7, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_gep_chain2(i64 %i, i64 %j, i32 %v) {
+; CHECK-LABEL: @split_dyn_gep_chain2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_2:%.*]] = alloca [4 x [4 x i32]], align 4
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_SROA_2_4_ELT_SROA_STRIDE:%.*]] = mul i64 [[I]], 16
+; CHECK-NEXT:    [[A_SROA_2_4_ELT_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_ELT_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_2_4_ELT_SROA_STRIDE1:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ELT_SROA_OFFSET2:%.*]] = add i64 [[A_SROA_2_4_ELT_SROA_OFFSET]], [[A_SROA_2_4_ELT_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_2_4_ELT_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_2]], i64 [[A_SROA_2_4_ELT_SROA_OFFSET2]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_2_4_ELT_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 5
+;
+entry:
+  %a = alloca %struct.TwoD, align 4
+  %ci = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 4
+  call void @llvm.assume(i1 %cj)
+  %arr = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 1
+  %row = getelementptr inbounds [4 x [4 x i32]], ptr %arr, i64 0, i64 %i
+  %elt = getelementptr inbounds [4 x i32], ptr %row, i64 0, i64 %j
+  store i32 %v, ptr %elt, align 4
+  %sc = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 0
+  store i32 5, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_select_fold(i64 %i, i32 %v, i1 %cond) {
+; CHECK-LABEL: @split_dyn_through_select_fold(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_0_4_SEL_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_SEL_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_SEL_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_4_SEL_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_SEL_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_4_SEL_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  %sel = select i1 %cond, ptr %arr, ptr %arr
+  store i32 %v, ptr %sel, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 2
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_addrspacecast(i64 %i, i32 %v) {
+; CHECK-LABEL: @split_dyn_through_addrspacecast(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_STRIDE1:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_OFFSET2:%.*]] = add i64 0, [[A_SROA_0_4_SC_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_SC_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_SROA_0_4_ASC_2_SROA_CAST:%.*]] = addrspacecast ptr [[A_SROA_0_4_SC_SROA_IDX3]] to ptr addrspace(2)
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr addrspace(2) [[A_SROA_0_4_ASC_2_SROA_CAST]], align 4
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_SC_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_SC_SROA_OFFSET]]
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_CAST:%.*]] = addrspacecast ptr [[A_SROA_0_4_SC_SROA_IDX]] to ptr addrspace(2)
+; CHECK-NEXT:    store i32 42, ptr addrspace(2) [[A_SROA_0_4_SC_SROA_CAST]], align 4
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_STRIDE2:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_OFFSET3:%.*]] = add i64 0, [[A_SROA_0_4_SC_SROA_STRIDE2]]
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_IDX4:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_SC_SROA_OFFSET3]]
+; CHECK-NEXT:    [[A_SROA_0_4_SC_SROA_CAST4:%.*]] = addrspacecast ptr [[A_SROA_0_4_SC_SROA_IDX4]] to ptr addrspace(2)
+; CHECK-NEXT:    [[A_SROA_0_4_LS:%.*]] = load i32, ptr addrspace(2) [[A_SROA_0_4_SC_SROA_CAST4]], align 4
+; CHECK-NEXT:    ret i32 [[A_SROA_0_4_LS]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %asc = addrspacecast ptr %a to ptr addrspace(1)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr addrspace(1) %asc, i64 0, i32 1, i64 %i
+  %asc.2 = addrspacecast ptr addrspace(1) %arr to ptr addrspace(2)
+  store i32 %v, ptr addrspace(2) %asc.2, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr addrspace(2) %asc.2, i64 0, i32 0
+  store i32 42, ptr addrspace(2) %sc, align 4
+  %ls = load i32, ptr addrspace(2) %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_phi_fold(i64 %i, i32 %v, i1 %cond) {
+; CHECK-LABEL: @split_dyn_through_phi_fold(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    br label [[JOIN:%.*]]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[JOIN]]
+; CHECK:       join:
+; CHECK-NEXT:    [[A_SROA_0_4_P_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_P_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_P_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_P_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_P_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_P_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  br i1 %cond, label %bb1, label %bb2
+bb1:
+  br label %join
+bb2:
+  br label %join
+join:
+  %p = phi ptr [ %arr, %bb1 ], [ %arr, %bb2 ]
+  store i32 %v, ptr %p, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 2
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_phi(i64 %i, i64 %j, i32 %v, i1 %cond) {
+; CHECK-LABEL: @split_dyn_through_phi(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_SROA_2_4_AI_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_AI_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_AI_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_AI_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_AI_SROA_OFFSET]]
+; CHECK-NEXT:    [[A_SROA_2_4_AJ_SROA_STRIDE:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_AJ_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_AJ_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_AJ_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_AJ_SROA_OFFSET]]
+; CHECK-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
+; CHECK:       bb1:
+; CHECK-NEXT:    br label [[JOIN:%.*]]
+; CHECK:       bb2:
+; CHECK-NEXT:    br label [[JOIN]]
+; CHECK:       join:
+; CHECK-NEXT:    [[P:%.*]] = phi ptr [ [[A_SROA_0_0_AI_SROA_IDX]], [[BB1]] ], [ [[A_SROA_0_0_AJ_SROA_IDX]], [[BB2]] ]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[P]], align 4
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %ci = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 8
+  call void @llvm.assume(i1 %cj)
+  %ai = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  %aj = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %j
+  br i1 %cond, label %bb1, label %bb2
+bb1:
+  br label %join
+bb2:
+  br label %join
+join:
+  %p = phi ptr [ %ai, %bb1 ], [ %aj, %bb2 ]
+  store i32 %v, ptr %p, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_select(i64 %i, i64 %j, i32 %v, i1 %cond) {
+; CHECK-LABEL: @split_dyn_through_select(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_SROA_0_4_AI_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_AI_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_AI_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_SEL_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_AI_SROA_OFFSET]]
+; CHECK-NEXT:    [[A_SROA_0_4_AJ_SROA_STRIDE:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_AJ_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_AJ_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_4_AJ_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_AJ_SROA_OFFSET]]
+; CHECK-NEXT:    br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]]
+; CHECK:       entry.then:
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_SEL_SROA_IDX]], align 4
+; CHECK-NEXT:    br label [[ENTRY_CONT:%.*]]
+; CHECK:       entry.else:
+; CHECK-NEXT:    store i32 [[V]], ptr [[A_SROA_0_4_AJ_SROA_IDX]], align 4
+; CHECK-NEXT:    br label [[ENTRY_CONT]]
+; CHECK:       entry.cont:
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %ci = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 8
+  call void @llvm.assume(i1 %cj)
+  %ai = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  %aj = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %j
+  %p = select i1 %cond, ptr %ai, ptr %aj
+  store i32 %v, ptr %p, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 2
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_two_arrays_memintrinsics(ptr %d, i64 %c) {
+; CHECK-LABEL: @split_two_arrays_memintrinsics(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[F_SROA_0:%.*]] = alloca [10 x i32], align 4
+; CHECK-NEXT:    [[F_SROA_5:%.*]] = alloca [15 x i32], align 4
+; CHECK-NEXT:    [[F_SROA_7:%.*]] = alloca [4 x i32], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[F_SROA_0]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[F_SROA_5]])
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[F_SROA_7]])
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[F_SROA_0]], i8 0, i64 40, i1 false)
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[F_SROA_5]], i8 0, i64 60, i1 false)
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[F_SROA_7]], i8 0, i64 16, i1 false)
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[F_SROA_0]], ptr align 4 [[D:%.*]], i64 40, i1 false)
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[F_SROA_5]], ptr align 4 [[D]], i64 60, i1 false)
+; CHECK-NEXT:    [[F_SROA_6_40_D_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[D]], i64 60
+; CHECK-NEXT:    [[F_SROA_6_40_COPYLOAD:%.*]] = load i32, ptr [[F_SROA_6_40_D_SROA_IDX]], align 4
+; CHECK-NEXT:    [[F_SROA_7_40_D_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[D]], i64 64
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[F_SROA_7]], ptr align 4 [[F_SROA_7_40_D_SROA_IDX]], i64 16, i1 false)
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[C:%.*]], 10
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[F_SROA_0_0_ARRAYIDX_SROA_STRIDE:%.*]] = mul i64 [[C]], 4
+; CHECK-NEXT:    [[F_SROA_0_0_ARRAYIDX_SROA_OFFSET:%.*]] = add i64 0, [[F_SROA_0_0_ARRAYIDX_SROA_STRIDE]]
+; CHECK-NEXT:    [[F_SROA_0_0_ARRAYIDX_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[F_SROA_0]], i64 [[F_SROA_0_0_ARRAYIDX_SROA_OFFSET]]
+; CHECK-NEXT:    [[F_SROA_0_0_:%.*]] = load i32, ptr [[F_SROA_0_0_ARRAYIDX_SROA_IDX]], align 4
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[F_SROA_0_0_]], [[F_SROA_6_40_COPYLOAD]]
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[F_SROA_0]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[F_SROA_5]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[F_SROA_7]])
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+entry:
+  %f = alloca %struct.TwoArrays, align 4
+  call void @llvm.lifetime.start.p0(ptr %f)
+  call void @llvm.memset.p0.i64(ptr align 4 %f, i8 0, i64 120, i1 false)
+  %data = getelementptr inbounds %struct.TwoArrays, ptr %f, i32 0, i32 0
+  %arraydecay = getelementptr inbounds [10 x i32], ptr %data, i64 0, i64 0
+  call void @llvm.memcpy.p0.p0.i64(ptr align 4 %arraydecay, ptr align 4 %d, i64 40, i1 false)
+  %buf = getelementptr inbounds %struct.TwoArrays, ptr %f, i32 0, i32 1
+  %arraydecay1 = getelementptr inbounds [20 x i32], ptr %buf, i64 0, i64 0
+  call void @llvm.memcpy.p0.p0.i64(ptr align 4 %arraydecay1, ptr align 4 %d, i64 80, i1 false)
+  %cmp = icmp ult i64 %c, 10
+  call void @llvm.assume(i1 %cmp)
+  %data2 = getelementptr inbounds %struct.TwoArrays, ptr %f, i32 0, i32 0
+  %arrayidx = getelementptr inbounds [10 x i32], ptr %data2, i64 0, i64 %c
+  %0 = load i32, ptr %arrayidx, align 4
+  %buf3 = getelementptr inbounds %struct.TwoArrays, ptr %f, i32 0, i32 1
+  %arrayidx4 = getelementptr inbounds [20 x i32], ptr %buf3, i64 0, i64 15
+  %1 = load i32, ptr %arrayidx4, align 4
+  %add = add nsw i32 %0, %1
+  call void @llvm.lifetime.end.p0(ptr %f)
+  ret i32 %add
+}
+
+define i32 @dyn_many_derived_ptrs(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_many_derived_ptrs(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_ARR_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_2_4_ARR_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_ARR_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %c0 = addrspacecast ptr %a to ptr addrspace(1)
+  %c1 = addrspacecast ptr addrspace(1) %c0 to ptr
+  %c2 = addrspacecast ptr %c1 to ptr addrspace(1)
+  %c3 = getelementptr inbounds %struct.ArrHolder, ptr addrspace(1) %c2, i64 0
+  %c4 = addrspacecast ptr addrspace(1) %c3 to ptr
+  %c5 = addrspacecast ptr %c4 to ptr addrspace(1)
+  %c6 = addrspacecast ptr addrspace(1) %c5 to ptr
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %c6, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %c6, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @split_dyn_through_bitcast(i64 %i, i32 %v) {
+; CHECK-LABEL: @split_dyn_through_bitcast(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_0_4_ARR_CAST_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_ARR_CAST_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_4_ARR_CAST_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_4_ARR_CAST_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_SROA_0_0_ARR_SROA_IDX]], align 4
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %bc = bitcast ptr %a to ptr
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %bc, i64 0, i32 1, i64 %i
+  %arr_cast = bitcast ptr %arr to ptr
+  store i32 %v, ptr %arr_cast, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 2
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @memset_dyn_const_len(i64 %i, i8 %b) {
+; CHECK-LABEL: @memset_dyn_const_len(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_2:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_ARR_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_2]], i64 [[A_SROA_2_4_ARR_SROA_OFFSET]]
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[A_SROA_2_4_ARR_SROA_IDX]], i8 [[B:%.*]], i64 4, i1 false)
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  call void @llvm.memset.p0.i64(ptr %arr, i8 %b, i64 4, i1 false)
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @memset_dyn_var_len(i64 %i, i8 %b, i64 %len) {
+; CHECK-LABEL: @memset_dyn_var_len(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_ARRHOLDER:%.*]], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[ARR:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I]]
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[ARR]], i8 [[B:%.*]], i64 [[LEN:%.*]], i1 false)
+; CHECK-NEXT:    [[SC:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 0
+; CHECK-NEXT:    store i32 42, ptr [[SC]], align 4
+; CHECK-NEXT:    [[LS:%.*]] = load i32, ptr [[SC]], align 4
+; CHECK-NEXT:    ret i32 [[LS]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  call void @llvm.memset.p0.i64(ptr %arr, i8 %b, i64 %len, i1 false)
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @memcpy_dyn_const_len(i64 %i, ptr %src) {
+; CHECK-LABEL: @memcpy_dyn_const_len(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_2:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_2_4_ARR_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_2_4_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_2]], i64 [[A_SROA_2_4_ARR_SROA_OFFSET]]
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[A_SROA_2_4_ARR_SROA_IDX]], ptr [[SRC:%.*]], i64 4, i1 false)
+; CHECK-NEXT:    ret i32 42
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  call void @llvm.memcpy.p0.p0.i64(ptr %arr, ptr %src, i64 4, i1 false)
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+; A memcpy with a non-constant length into a dynamically-indexed destination
+; is bailed before the mem-transfer bookkeeping, leaving the alloca unsplit.
+define i32 @memcpy_dyn_var_len(i64 %i, ptr %src, i64 %len) {
+; CHECK-LABEL: @memcpy_dyn_var_len(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_ARRHOLDER:%.*]], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[ARR:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I]]
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[ARR]], ptr [[SRC:%.*]], i64 [[LEN:%.*]], i1 false)
+; CHECK-NEXT:    [[SC:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 0
+; CHECK-NEXT:    store i32 42, ptr [[SC]], align 4
+; CHECK-NEXT:    [[LS:%.*]] = load i32, ptr [[SC]], align 4
+; CHECK-NEXT:    ret i32 [[LS]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  call void @llvm.memcpy.p0.p0.i64(ptr %arr, ptr %src, i64 %len, i1 false)
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @no_assume_no_split(i64 %i, i32 %v) {
+; CHECK-LABEL: @no_assume_no_split(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_ARRHOLDER:%.*]], align 4
+; CHECK-NEXT:    [[ARR:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I:%.*]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[ARR]], align 4
+; CHECK-NEXT:    [[SUM:%.*]] = load i32, ptr [[ARR]], align 4
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %la = load i32, ptr %arr, align 4
+  ret i32 %la
+}
+
+define i32 @dyn_oob_range(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_oob_range(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca { [8 x i32], i32 }, align 8
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 10000
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_1_4_ARR_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_1_4_ARR_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_1_4_ARR_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_0_ARR_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_SROA_1_4_ARR_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_0_ARR_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_32_SC_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 32
+; CHECK-NEXT:    store i32 42, ptr [[A_32_SC_SROA_IDX]], align 8
+; CHECK-NEXT:    [[A_SROA_0_4_ARR_SROA_STRIDE1:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_SROA_0_4_ARR_SROA_OFFSET2:%.*]] = add i64 0, [[A_SROA_0_4_ARR_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_0_4_ARR_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_SROA_0_4_ARR_SROA_OFFSET2]]
+; CHECK-NEXT:    [[SUM:%.*]] = load i32, ptr [[A_SROA_0_4_ARR_SROA_IDX3]], align 4
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 10000
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 2
+  store i32 42, ptr %sc, align 4
+  %ld = load i32, ptr %arr, align 4
+  ret i32 %ld
+}
+
+define i32 @dyn_assume_const(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_assume_const(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    ret i32 [[SUM:%.*]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp eq i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %ld = load i32, ptr %arr, align 4
+  ret i32 %ld
+}
+
+define i32 @dyn_partial_bounded_index(i64 %i, i64 %j, i32 %v) {
+; CHECK-LABEL: @dyn_partial_bounded_index(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_TWOD:%.*]], align 4
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds [[STRUCT_TWOD]], ptr [[A]], i64 0, i32 1, i64 [[I]], i64 [[J:%.*]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[P]], align 4
+; CHECK-NEXT:    [[SC:%.*]] = getelementptr inbounds [[STRUCT_TWOD]], ptr [[A]], i64 0, i32 0
+; CHECK-NEXT:    store i32 7, ptr [[SC]], align 4
+; CHECK-NEXT:    [[LS:%.*]] = load i32, ptr [[SC]], align 4
+; CHECK-NEXT:    ret i32 [[LS]]
+;
+entry:
+  %a = alloca %struct.TwoD, align 4
+  %ci = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %ci)
+  %p = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 1, i64 %i, i64 %j
+  store i32 %v, ptr %p, align 4
+  %sc = getelementptr inbounds %struct.TwoD, ptr %a, i64 0, i32 0
+  store i32 7, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i32 @dyn_negative_low_index(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_negative_low_index(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_ARRHOLDER:%.*]], align 4
+; CHECK-NEXT:    [[LO:%.*]] = icmp sge i64 [[I:%.*]], -4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[LO]])
+; CHECK-NEXT:    [[HI:%.*]] = icmp slt i64 [[I]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[HI]])
+; CHECK-NEXT:    [[ARR:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[ARR]], align 4
+; CHECK-NEXT:    [[RD:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I]]
+; CHECK-NEXT:    [[LV:%.*]] = load i32, ptr [[RD]], align 4
+; CHECK-NEXT:    ret i32 [[LV]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %lo = icmp sge i64 %i, -4
+  call void @llvm.assume(i1 %lo)
+  %hi = icmp slt i64 %i, 8
+  call void @llvm.assume(i1 %hi)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %rd = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  %lv = load i32, ptr %rd, align 4
+  ret i32 %lv
+}
+
+define i32 @dyn_range_below_base(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_range_below_base(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_ARRHOLDER:%.*]], align 4
+; CHECK-NEXT:    [[LO:%.*]] = icmp sge i64 [[I:%.*]], -8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[LO]])
+; CHECK-NEXT:    [[HI:%.*]] = icmp slt i64 [[I]], -1
+; CHECK-NEXT:    call void @llvm.assume(i1 [[HI]])
+; CHECK-NEXT:    [[ARR:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 1, i64 [[I]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[ARR]], align 4
+; CHECK-NEXT:    [[SC:%.*]] = getelementptr inbounds [[STRUCT_ARRHOLDER]], ptr [[A]], i64 0, i32 0
+; CHECK-NEXT:    store i32 42, ptr [[SC]], align 4
+; CHECK-NEXT:    [[LS:%.*]] = load i32, ptr [[SC]], align 4
+; CHECK-NEXT:    ret i32 [[LS]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %lo = icmp sge i64 %i, -8
+  call void @llvm.assume(i1 %lo)
+  %hi = icmp slt i64 %i, -1
+  call void @llvm.assume(i1 %hi)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  store i32 %v, ptr %arr, align 4
+  %sc = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 0
+  store i32 42, ptr %sc, align 4
+  %ls = load i32, ptr %sc, align 4
+  ret i32 %ls
+}
+
+define i8 @dyn_no_int_widen(i64 %i) {
+; CHECK-LABEL: @dyn_no_int_widen(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 66051, ptr [[A_SROA_0]], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_0_0_GEP_SROA_STRIDE:%.*]] = mul i64 [[I]], 1
+; CHECK-NEXT:    [[A_SROA_0_0_GEP_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_0_GEP_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_0_GEP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_0_GEP_SROA_OFFSET]]
+; CHECK-NEXT:    [[A_SROA_0_0_V:%.*]] = load i8, ptr [[A_SROA_0_0_GEP_SROA_IDX]], align 1
+; CHECK-NEXT:    ret i8 [[A_SROA_0_0_V]]
+;
+entry:
+  %a = alloca [4 x i8], align 4
+  store i32 66051, ptr %a, align 4
+  %c = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %c)
+  %gep = getelementptr inbounds i8, ptr %a, i64 %i
+  %v = load i8, ptr %gep, align 1
+  ret i8 %v
+}
+
+define <4 x float> @dyn_no_vec_promote(i64 %i, float %v) {
+; CHECK-LABEL: @dyn_no_vec_promote(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca <4 x float>, align 16
+; CHECK-NEXT:    store <4 x float> zeroinitializer, ptr [[A]], align 16
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET:%.*]] = add i64 0, [[A_0_GEP_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_GEP_SROA_OFFSET]]
+; CHECK-NEXT:    store float [[V:%.*]], ptr [[A_0_GEP_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_0_R:%.*]] = load <4 x float>, ptr [[A]], align 16
+; CHECK-NEXT:    ret <4 x float> [[A_0_R]]
+;
+entry:
+  %a = alloca <4 x float>, align 16
+  store <4 x float> zeroinitializer, ptr %a, align 16
+  %c = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %c)
+  %gep = getelementptr inbounds float, ptr %a, i64 %i
+  store float %v, ptr %gep, align 4
+  %r = load <4 x float>, ptr %a, align 16
+  ret <4 x float> %r
+}
+
+define i32 @dyn_align_small_stride(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_align_small_stride(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [8 x i32], align 16
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET:%.*]] = add i64 0, [[A_0_GEP_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_GEP_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_0_GEP_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE1:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET2:%.*]] = add i64 0, [[A_0_GEP_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_GEP_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_0_L:%.*]] = load i32, ptr [[A_0_GEP_SROA_IDX3]], align 4
+; CHECK-NEXT:    ret i32 [[A_0_L]]
+;
+entry:
+  %a = alloca [8 x i32], align 16
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %gep = getelementptr inbounds [8 x i32], ptr %a, i64 0, i64 %i
+  store i32 %v, ptr %gep, align 4
+  %l = load i32, ptr %gep, align 4
+  ret i32 %l
+}
+
+define i32 @dyn_align_multiple_strides(i64 %i, i64 %j, i32 %v) {
+; CHECK-LABEL: @dyn_align_multiple_strides(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [4 x [4 x i32]], align 16
+; CHECK-NEXT:    [[CI:%.*]] = icmp ult i64 [[I:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CI]])
+; CHECK-NEXT:    [[CJ:%.*]] = icmp ult i64 [[J:%.*]], 4
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CJ]])
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE:%.*]] = mul i64 [[I]], 16
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET:%.*]] = add i64 0, [[A_0_GEP_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE1:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET2:%.*]] = add i64 [[A_0_GEP_SROA_OFFSET]], [[A_0_GEP_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_GEP_SROA_OFFSET2]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_0_GEP_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE3:%.*]] = mul i64 [[I]], 16
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET4:%.*]] = add i64 0, [[A_0_GEP_SROA_STRIDE3]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_STRIDE5:%.*]] = mul i64 [[J]], 4
+; CHECK-NEXT:    [[A_0_GEP_SROA_OFFSET6:%.*]] = add i64 [[A_0_GEP_SROA_OFFSET4]], [[A_0_GEP_SROA_STRIDE5]]
+; CHECK-NEXT:    [[A_0_GEP_SROA_IDX7:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_GEP_SROA_OFFSET6]]
+; CHECK-NEXT:    [[A_0_L:%.*]] = load i32, ptr [[A_0_GEP_SROA_IDX7]], align 4
+; CHECK-NEXT:    ret i32 [[A_0_L]]
+;
+entry:
+  %a = alloca [4 x [4 x i32]], align 16
+  %ci = icmp ult i64 %i, 4
+  call void @llvm.assume(i1 %ci)
+  %cj = icmp ult i64 %j, 4
+  call void @llvm.assume(i1 %cj)
+  %gep = getelementptr inbounds [4 x [4 x i32]], ptr %a, i64 0, i64 %i, i64 %j
+  store i32 %v, ptr %gep, align 4
+  %l = load i32, ptr %gep, align 4
+  ret i32 %l
+}
+
+define i64 @dyn_align_const_plus_stride(i64 %i, i64 %v) {
+; CHECK-LABEL: @dyn_align_const_plus_stride(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_0:%.*]] = alloca [8 x i64], align 8
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_STRIDE:%.*]] = mul i64 [[I]], 8
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_OFFSET:%.*]] = add i64 0, [[A_SROA_0_8_GEP_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_8_GEP_SROA_OFFSET]]
+; CHECK-NEXT:    store i64 [[V:%.*]], ptr [[A_SROA_0_8_GEP_SROA_IDX]], align 8
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_STRIDE1:%.*]] = mul i64 [[I]], 8
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_OFFSET2:%.*]] = add i64 0, [[A_SROA_0_8_GEP_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_0_8_GEP_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_0]], i64 [[A_SROA_0_8_GEP_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_SROA_0_8_L:%.*]] = load i64, ptr [[A_SROA_0_8_GEP_SROA_IDX3]], align 8
+; CHECK-NEXT:    ret i64 [[A_SROA_0_8_L]]
+;
+entry:
+  %a = alloca { i64, [8 x i64] }, align 8
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %gep = getelementptr inbounds { i64, [8 x i64] }, ptr %a, i64 0, i32 1, i64 %i
+  store i64 %v, ptr %gep, align 8
+  %l = load i64, ptr %gep, align 8
+  ret i64 %l
+}
+
+define <8 x float> @dyn_no_tree_merge(i64 %k, <4 x float> %v0, <2 x float> %v1) {
+; CHECK-LABEL: @dyn_no_tree_merge(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[K:%.*]], 2
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A:%.*]] = alloca <8 x float>, align 32
+; CHECK-NEXT:    store <4 x float> [[V0:%.*]], ptr [[A]], align 32
+; CHECK-NEXT:    [[A_16_G_SROA_STRIDE:%.*]] = mul i64 [[K]], 8
+; CHECK-NEXT:    [[A_16_G_SROA_OFFSET:%.*]] = add i64 16, [[A_16_G_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_16_G_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_16_G_SROA_OFFSET]]
+; CHECK-NEXT:    store <2 x float> [[V1:%.*]], ptr [[A_16_G_SROA_IDX]], align 8
+; CHECK-NEXT:    [[A_0_R:%.*]] = load <8 x float>, ptr [[A]], align 32
+; CHECK-NEXT:    ret <8 x float> [[A_0_R]]
+;
+entry:
+  %c = icmp ult i64 %k, 2
+  call void @llvm.assume(i1 %c)
+  %a = alloca <8 x float>, align 32
+  store <4 x float> %v0, ptr %a, align 32
+  %base = getelementptr inbounds i8, ptr %a, i64 16
+  %g = getelementptr inbounds <2 x float>, ptr %base, i64 %k
+  store <2 x float> %v1, ptr %g, align 8
+  %r = load <8 x float>, ptr %a, align 32
+  ret <8 x float> %r
+}
+
+define i32 @split_dyn_narrow_addrspacecast(i64 %i, i32 %v) {
+; CHECK-LABEL: @split_dyn_narrow_addrspacecast(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A_SROA_1:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[TMP0:%.*]] = trunc i64 [[I]] to i32
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_STRIDE:%.*]] = mul i32 [[TMP0]], 4
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_OFFSET:%.*]] = add i32 0, [[A_SROA_1_4_ASC_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_1]], i32 [[A_SROA_1_4_ASC_SROA_OFFSET]]
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_CAST:%.*]] = addrspacecast ptr [[A_SROA_1_4_ASC_SROA_IDX]] to ptr addrspace(7)
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr addrspace(7) [[A_SROA_1_4_ASC_SROA_CAST]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = trunc i64 [[I]] to i32
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_STRIDE1:%.*]] = mul i32 [[TMP1]], 4
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_OFFSET2:%.*]] = add i32 0, [[A_SROA_1_4_ASC_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A_SROA_1]], i32 [[A_SROA_1_4_ASC_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_SROA_1_4_ASC_SROA_CAST4:%.*]] = addrspacecast ptr [[A_SROA_1_4_ASC_SROA_IDX3]] to ptr addrspace(7)
+; CHECK-NEXT:    [[A_SROA_1_4_LD:%.*]] = load i32, ptr addrspace(7) [[A_SROA_1_4_ASC_SROA_CAST4]], align 4
+; CHECK-NEXT:    ret i32 [[A_SROA_1_4_LD]]
+;
+entry:
+  %a = alloca %struct.ArrHolder, align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %arr = getelementptr inbounds %struct.ArrHolder, ptr %a, i64 0, i32 1, i64 %i
+  %asc = addrspacecast ptr %arr to ptr addrspace(7)
+  store i32 %v, ptr addrspace(7) %asc, align 4
+  %ld = load i32, ptr addrspace(7) %asc, align 4
+  ret i32 %ld
+}
+
+define i32 @dyn_whole_alloca_array(i64 %i, i32 %v) {
+; CHECK-LABEL: @dyn_whole_alloca_array(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = alloca [8 x i32], align 4
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i64 [[I:%.*]], 8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[A_0_G_SROA_STRIDE:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_0_G_SROA_OFFSET:%.*]] = add i64 0, [[A_0_G_SROA_STRIDE]]
+; CHECK-NEXT:    [[A_0_G_SROA_IDX:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_G_SROA_OFFSET]]
+; CHECK-NEXT:    store i32 [[V:%.*]], ptr [[A_0_G_SROA_IDX]], align 4
+; CHECK-NEXT:    [[A_0_G_SROA_STRIDE1:%.*]] = mul i64 [[I]], 4
+; CHECK-NEXT:    [[A_0_G_SROA_OFFSET2:%.*]] = add i64 0, [[A_0_G_SROA_STRIDE1]]
+; CHECK-NEXT:    [[A_0_G_SROA_IDX3:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[A_0_G_SROA_OFFSET2]]
+; CHECK-NEXT:    [[A_0_LD:%.*]] = load i32, ptr [[A_0_G_SROA_IDX3]], align 4
+; CHECK-NEXT:    ret i32 [[A_0_LD]]
+;
+entry:
+  %a = alloca [8 x i32], align 4
+  %c = icmp ult i64 %i, 8
+  call void @llvm.assume(i1 %c)
+  %g = getelementptr inbounds [8 x i32], ptr %a, i64 0, i64 %i
+  store i32 %v, ptr %g, align 4
+  %ld = load i32, ptr %g, align 4
+  ret i32 %ld
+}
diff --git a/llvm/test/Transforms/SROA/select-gep.ll b/llvm/test/Transforms/SROA/select-gep.ll
index 688ea03cddac0..deba5c9e5075a 100644
--- a/llvm/test/Transforms/SROA/select-gep.ll
+++ b/llvm/test/Transforms/SROA/select-gep.ll
@@ -274,16 +274,18 @@ define i32 @test_select_idx_escaped(i1 %c, ptr %p) {
 ; constants?
 define i32 @test_select_idx_nested(i1 %c, i1 %c2) {
 ; CHECK-LABEL: @test_select_idx_nested(
-; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca [20 x i64], align 8
-; CHECK-NEXT:    store i32 1, ptr [[ALLOCA]], align 4
+; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca [28 x i8], align 8
+; CHECK-NEXT:    store i32 1, ptr [[ALLOCA]], align 8
 ; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr inbounds i8, ptr [[ALLOCA]], i64 8
-; CHECK-NEXT:    store i32 2, ptr [[GEP1]], align 4
+; CHECK-NEXT:    store i32 2, ptr [[GEP1]], align 8
 ; CHECK-NEXT:    [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[ALLOCA]], i64 24
-; CHECK-NEXT:    store i32 3, ptr [[GEP2]], align 4
+; CHECK-NEXT:    store i32 3, ptr [[GEP2]], align 8
 ; CHECK-NEXT:    [[IDX1:%.*]] = select i1 [[C:%.*]], i64 24, i64 0
 ; CHECK-NEXT:    [[IDX2:%.*]] = select i1 [[C2:%.*]], i64 [[IDX1]], i64 8
-; CHECK-NEXT:    [[GEP3:%.*]] = getelementptr inbounds i8, ptr [[ALLOCA]], i64 [[IDX2]]
-; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[GEP3]], align 4
+; CHECK-NEXT:    [[ALLOCA_SROA_0_0_GEP3_SROA_STRIDE:%.*]] = mul i64 [[IDX2]], 1
+; CHECK-NEXT:    [[ALLOCA_SROA_0_0_GEP3_SROA_OFFSET:%.*]] = add i64 0, [[ALLOCA_SROA_0_0_GEP3_SROA_STRIDE]]
+; CHECK-NEXT:    [[GEP3:%.*]] = getelementptr inbounds i8, ptr [[ALLOCA]], i64 [[ALLOCA_SROA_0_0_GEP3_SROA_OFFSET]]
+; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[GEP3]], align 1
 ; CHECK-NEXT:    ret i32 [[RES]]
 ;
   %alloca = alloca [20 x i64], align 8



More information about the llvm-commits mailing list