[llvm] [SROA] Pre-split overlapping move slices within a partition (PR #210061)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 16 06:40:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: John Brawn (john-brawn-arm)
<details>
<summary>Changes</summary>
When we have an overlapping load and store within an alloca, e.g. due to a memmove that has been transformed into a load+store by InstCombine, then currently SROA can't do anything with it. By pre-splitting such loads and stores we eliminate any overlap between slices and thus SROA can optimize them.
---
Patch is 29.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/210061.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/SROA.cpp (+71-2)
- (added) llvm/test/Transforms/SROA/load-store-overlap.ll (+474)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index bae6e766c5ee3..e9a4eb4dc7d21 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -4805,6 +4805,43 @@ static Type *getTypePartition(const DataLayout &DL, Type *Ty, uint64_t Offset,
return SubTy;
}
+/// Try to find a slice in P that overlaps with S, and which forms a load/store
+/// pair with S (i.e. the two slices are used to perform a memmove-like copy
+/// within P).
+static Slice *findOverlappingCopySlice(Slice &S, Partition &P) {
+ // Single byte slices can't overlap anything
+ if (S.endOffset() - S.beginOffset() == 1)
+ return nullptr;
+ // The source/destination of this slice needs to be a memory instruction
+ // whose slice overlaps this one.
+ Instruction *I = cast<Instruction>(S.getUse()->getUser());
+ Instruction *J = nullptr;
+ if (auto *LI = dyn_cast<LoadInst>(I)) {
+ if (!LI->hasOneUser())
+ return nullptr;
+ J = dyn_cast<Instruction>(*LI->user_begin());
+ if (!J)
+ return nullptr;
+ } else if (auto *SI = dyn_cast<StoreInst>(I)) {
+ J = dyn_cast<Instruction>(SI->getValueOperand());
+ if (!J || !J->hasOneUser())
+ return nullptr;
+ } else {
+ return nullptr;
+ }
+ // Check if there's a slice that corresponds to J that overlaps this slice
+ for (Slice &JS : P) {
+ if (JS.getUse()->getUser() != J)
+ continue;
+ if (S.beginOffset() > JS.beginOffset() && S.beginOffset() < JS.endOffset())
+ return &JS;
+ if (JS.beginOffset() > S.beginOffset() && JS.beginOffset() < S.endOffset())
+ return &JS;
+ return nullptr;
+ }
+ return nullptr;
+}
+
/// Pre-split loads and stores to simplify rewriting.
///
/// We want to break up the splittable load+store pairs as much as
@@ -4867,7 +4904,9 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
for (auto &P : AS.partitions()) {
for (Slice &S : P) {
Instruction *I = cast<Instruction>(S.getUse()->getUser());
- if (!S.isSplittable() || S.endOffset() <= P.endOffset()) {
+ bool ExtendsPastPartitionEnd = S.endOffset() > P.endOffset();
+ Slice *CopyOverlap = findOverlappingCopySlice(S, P);
+ if (!S.isSplittable() || !(ExtendsPastPartitionEnd || CopyOverlap)) {
// If this is a load we have to track that it can't participate in any
// pre-splitting. If this is a store of a load we have to track that
// that load also can't participate in any pre-splitting.
@@ -4923,7 +4962,37 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
assert(Offsets.Splits.empty() &&
"Should not have splits the first time we see an instruction!");
Offsets.S = &S;
- Offsets.Splits.push_back(P.endOffset() - S.beginOffset());
+ if (CopyOverlap) {
+ // S is being moved to CopyOverlap, which overlaps with S, so we split S
+ // into three part, which:
+ // * Starts outside the overlap and is copied into the overlap
+ // * Either starts inside the overlap and is copied inside the overlap,
+ // or starts outside and is copied outside, depending on if the
+ // overlap or non-overlap area is larger, and which will be empty if
+ // they are equal.
+ // * Starts inside the overlap and is copied outside the overlap
+ // This should result in the first and last parts being promoted to
+ // scalars, which may result in the middle part now being an overlapping
+ // copy which will then be presplit in the same way in the next
+ // iteration of the SROA loop.
+ uint64_t OverlapStart =
+ std::max(S.beginOffset(), CopyOverlap->beginOffset());
+ uint64_t OverlapEnd = std::min(S.endOffset(), CopyOverlap->endOffset());
+ uint64_t OverlapSize = OverlapEnd - OverlapStart;
+ uint64_t NonOverlapSize =
+ (S.endOffset() - S.beginOffset()) - OverlapSize;
+ if (OverlapSize < NonOverlapSize) {
+ Offsets.Splits.push_back(OverlapSize);
+ Offsets.Splits.push_back(NonOverlapSize);
+ } else if (OverlapSize > NonOverlapSize) {
+ Offsets.Splits.push_back(NonOverlapSize);
+ Offsets.Splits.push_back(OverlapSize);
+ } else {
+ Offsets.Splits.push_back(OverlapSize);
+ }
+ } else {
+ Offsets.Splits.push_back(P.endOffset() - S.beginOffset());
+ }
}
// Now scan the already split slices, and add a split for any of them which
diff --git a/llvm/test/Transforms/SROA/load-store-overlap.ll b/llvm/test/Transforms/SROA/load-store-overlap.ll
new file mode 100644
index 0000000000000..d58275ad33e25
--- /dev/null
+++ b/llvm/test/Transforms/SROA/load-store-overlap.ll
@@ -0,0 +1,474 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=sroa < %s | FileCheck %s
+
+; In these tests we have memmove-like loads and stores within an alloca, where
+; the load slice overlaps with the store slice.
+
+define void @move_up(i8 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_up(
+; CHECK-SAME: i8 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 2
+; CHECK-NEXT: [[DEST_3:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 3
+; CHECK-NEXT: [[DEST_4:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4
+; CHECK-NEXT: store i8 [[ARG]], ptr [[DEST]], align 1
+; CHECK-NEXT: store i8 0, ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i8 1, ptr [[DEST_2]], align 1
+; CHECK-NEXT: store i8 2, ptr [[DEST_3]], align 1
+; CHECK-NEXT: store i8 3, ptr [[DEST_4]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [5 x i8], align 1
+ %arr.1 = getelementptr inbounds nuw i8, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i8, ptr %arr, i64 2
+ %arr.3 = getelementptr inbounds nuw i8, ptr %arr, i64 3
+ %arr.4 = getelementptr inbounds nuw i8, ptr %arr, i64 4
+ store i8 0, ptr %arr, align 1
+ store i8 1, ptr %arr.1, align 1
+ store i8 2, ptr %arr.2, align 1
+ store i8 3, ptr %arr.3, align 1
+ store i8 4, ptr %arr.4, align 1
+ %move = load i32, ptr %arr, align 1
+ store i32 %move, ptr %arr.1, align 1
+ store i8 %arg, ptr %arr, align 1
+ %dest.1 = getelementptr inbounds nuw i8, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i8, ptr %dest, i64 2
+ %dest.3 = getelementptr inbounds nuw i8, ptr %dest, i64 3
+ %dest.4 = getelementptr inbounds nuw i8, ptr %dest, i64 4
+ %val.0 = load i8, ptr %arr, align 1
+ %val.1 = load i8, ptr %arr.1, align 1
+ %val.2 = load i8, ptr %arr.2, align 1
+ %val.3 = load i8, ptr %arr.3, align 1
+ %val.4 = load i8, ptr %arr.4, align 1
+ store i8 %val.0, ptr %dest, align 1
+ store i8 %val.1, ptr %dest.1, align 1
+ store i8 %val.2, ptr %dest.2, align 1
+ store i8 %val.3, ptr %dest.3, align 1
+ store i8 %val.4, ptr %dest.4, align 1
+ ret void
+}
+
+define void @move_down(i8 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_down(
+; CHECK-SAME: i8 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 2
+; CHECK-NEXT: [[DEST_3:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 3
+; CHECK-NEXT: [[DEST_4:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4
+; CHECK-NEXT: store i8 1, ptr [[DEST]], align 1
+; CHECK-NEXT: store i8 2, ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i8 3, ptr [[DEST_2]], align 1
+; CHECK-NEXT: store i8 4, ptr [[DEST_3]], align 1
+; CHECK-NEXT: store i8 [[ARG]], ptr [[DEST_4]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [5 x i8], align 1
+ %arr.1 = getelementptr inbounds nuw i8, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i8, ptr %arr, i64 2
+ %arr.3 = getelementptr inbounds nuw i8, ptr %arr, i64 3
+ %arr.4 = getelementptr inbounds nuw i8, ptr %arr, i64 4
+ store i8 0, ptr %arr, align 1
+ store i8 1, ptr %arr.1, align 1
+ store i8 2, ptr %arr.2, align 1
+ store i8 3, ptr %arr.3, align 1
+ store i8 4, ptr %arr.4, align 1
+ %move = load i32, ptr %arr.1, align 1
+ store i32 %move, ptr %arr, align 1
+ store i8 %arg, ptr %arr.4, align 1
+ %dest.1 = getelementptr inbounds nuw i8, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i8, ptr %dest, i64 2
+ %dest.3 = getelementptr inbounds nuw i8, ptr %dest, i64 3
+ %dest.4 = getelementptr inbounds nuw i8, ptr %dest, i64 4
+ %val.0 = load i8, ptr %arr, align 1
+ %val.1 = load i8, ptr %arr.1, align 1
+ %val.2 = load i8, ptr %arr.2, align 1
+ %val.3 = load i8, ptr %arr.3, align 1
+ %val.4 = load i8, ptr %arr.4, align 1
+ store i8 %val.0, ptr %dest, align 1
+ store i8 %val.1, ptr %dest.1, align 1
+ store i8 %val.2, ptr %dest.2, align 1
+ store i8 %val.3, ptr %dest.3, align 1
+ store i8 %val.4, ptr %dest.4, align 1
+ ret void
+}
+
+define void @move_up_small_overlap(i8 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_up_small_overlap(
+; CHECK-SAME: i8 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[ARR_SROA_4_1_INSERT_MASK:%.*]] = and i16 undef, -256
+; CHECK-NEXT: [[ARR_SROA_4_1_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_4_1_INSERT_MASK]], 1
+; CHECK-NEXT: [[ARR_SROA_4_2_INSERT_MASK:%.*]] = and i16 [[ARR_SROA_4_1_INSERT_INSERT]], 255
+; CHECK-NEXT: [[ARR_SROA_4_2_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_4_2_INSERT_MASK]], 512
+; CHECK-NEXT: [[ARR_SROA_13_4_INSERT_MASK:%.*]] = and i16 undef, -256
+; CHECK-NEXT: [[ARR_SROA_13_4_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_13_4_INSERT_MASK]], 4
+; CHECK-NEXT: [[ARR_SROA_13_5_INSERT_MASK:%.*]] = and i16 [[ARR_SROA_13_4_INSERT_INSERT]], 255
+; CHECK-NEXT: [[ARR_SROA_13_5_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_13_5_INSERT_MASK]], 1280
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 2
+; CHECK-NEXT: [[DEST_3:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 3
+; CHECK-NEXT: [[DEST_4:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4
+; CHECK-NEXT: [[DEST_5:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 5
+; CHECK-NEXT: [[DEST_6:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 6
+; CHECK-NEXT: [[ARR_SROA_4_1_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_4_2_INSERT_INSERT]] to i8
+; CHECK-NEXT: [[ARR_SROA_4_2_EXTRACT_SHIFT:%.*]] = lshr i16 [[ARR_SROA_4_2_INSERT_INSERT]], 8
+; CHECK-NEXT: [[ARR_SROA_4_2_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_4_2_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: [[ARR_SROA_13_4_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_4_2_INSERT_INSERT]] to i8
+; CHECK-NEXT: [[ARR_SROA_13_5_EXTRACT_SHIFT:%.*]] = lshr i16 [[ARR_SROA_4_2_INSERT_INSERT]], 8
+; CHECK-NEXT: [[ARR_SROA_13_5_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_13_5_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: store i8 [[ARG]], ptr [[DEST]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_4_1_EXTRACT_TRUNC]], ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_4_2_EXTRACT_TRUNC]], ptr [[DEST_2]], align 1
+; CHECK-NEXT: store i8 0, ptr [[DEST_3]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_13_4_EXTRACT_TRUNC]], ptr [[DEST_4]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_13_5_EXTRACT_TRUNC]], ptr [[DEST_5]], align 1
+; CHECK-NEXT: store i8 3, ptr [[DEST_6]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [8 x i8], align 1
+ %arr.1 = getelementptr inbounds nuw i8, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i8, ptr %arr, i64 2
+ %arr.3 = getelementptr inbounds nuw i8, ptr %arr, i64 3
+ %arr.4 = getelementptr inbounds nuw i8, ptr %arr, i64 4
+ %arr.5 = getelementptr inbounds nuw i8, ptr %arr, i64 5
+ %arr.6 = getelementptr inbounds nuw i8, ptr %arr, i64 6
+ store i8 0, ptr %arr, align 1
+ store i8 1, ptr %arr.1, align 1
+ store i8 2, ptr %arr.2, align 1
+ store i8 3, ptr %arr.3, align 1
+ store i8 4, ptr %arr.4, align 1
+ store i8 5, ptr %arr.5, align 1
+ store i8 6, ptr %arr.6, align 1
+ %move = load i32, ptr %arr, align 1
+ store i32 %move, ptr %arr.3, align 1
+ store i8 %arg, ptr %arr, align 1
+ %dest.1 = getelementptr inbounds nuw i8, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i8, ptr %dest, i64 2
+ %dest.3 = getelementptr inbounds nuw i8, ptr %dest, i64 3
+ %dest.4 = getelementptr inbounds nuw i8, ptr %dest, i64 4
+ %dest.5 = getelementptr inbounds nuw i8, ptr %dest, i64 5
+ %dest.6 = getelementptr inbounds nuw i8, ptr %dest, i64 6
+ %val.0 = load i8, ptr %arr, align 1
+ %val.1 = load i8, ptr %arr.1, align 1
+ %val.2 = load i8, ptr %arr.2, align 1
+ %val.3 = load i8, ptr %arr.3, align 1
+ %val.4 = load i8, ptr %arr.4, align 1
+ %val.5 = load i8, ptr %arr.5, align 1
+ %val.6 = load i8, ptr %arr.6, align 1
+ store i8 %val.0, ptr %dest, align 1
+ store i8 %val.1, ptr %dest.1, align 1
+ store i8 %val.2, ptr %dest.2, align 1
+ store i8 %val.3, ptr %dest.3, align 1
+ store i8 %val.4, ptr %dest.4, align 1
+ store i8 %val.5, ptr %dest.5, align 1
+ store i8 %val.6, ptr %dest.6, align 1
+ ret void
+}
+
+define void @move_down_small_overlap(i8 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_down_small_overlap(
+; CHECK-SAME: i8 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[ARR_SROA_3_1_INSERT_MASK:%.*]] = and i16 undef, -256
+; CHECK-NEXT: [[ARR_SROA_3_1_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_3_1_INSERT_MASK]], 1
+; CHECK-NEXT: [[ARR_SROA_3_2_INSERT_MASK:%.*]] = and i16 [[ARR_SROA_3_1_INSERT_INSERT]], 255
+; CHECK-NEXT: [[ARR_SROA_3_2_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_3_2_INSERT_MASK]], 512
+; CHECK-NEXT: [[ARR_SROA_12_4_INSERT_MASK:%.*]] = and i16 undef, -256
+; CHECK-NEXT: [[ARR_SROA_12_4_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_12_4_INSERT_MASK]], 4
+; CHECK-NEXT: [[ARR_SROA_12_5_INSERT_MASK:%.*]] = and i16 [[ARR_SROA_12_4_INSERT_INSERT]], 255
+; CHECK-NEXT: [[ARR_SROA_12_5_INSERT_INSERT:%.*]] = or i16 [[ARR_SROA_12_5_INSERT_MASK]], 1280
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 2
+; CHECK-NEXT: [[DEST_3:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 3
+; CHECK-NEXT: [[DEST_4:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4
+; CHECK-NEXT: [[DEST_5:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 5
+; CHECK-NEXT: [[DEST_6:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 6
+; CHECK-NEXT: [[ARR_SROA_3_1_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_12_5_INSERT_INSERT]] to i8
+; CHECK-NEXT: [[ARR_SROA_3_2_EXTRACT_SHIFT:%.*]] = lshr i16 [[ARR_SROA_12_5_INSERT_INSERT]], 8
+; CHECK-NEXT: [[ARR_SROA_3_2_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_3_2_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: [[ARR_SROA_12_4_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_12_5_INSERT_INSERT]] to i8
+; CHECK-NEXT: [[ARR_SROA_12_5_EXTRACT_SHIFT:%.*]] = lshr i16 [[ARR_SROA_12_5_INSERT_INSERT]], 8
+; CHECK-NEXT: [[ARR_SROA_12_5_EXTRACT_TRUNC:%.*]] = trunc i16 [[ARR_SROA_12_5_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: store i8 3, ptr [[DEST]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_3_1_EXTRACT_TRUNC]], ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_3_2_EXTRACT_TRUNC]], ptr [[DEST_2]], align 1
+; CHECK-NEXT: store i8 6, ptr [[DEST_3]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_12_4_EXTRACT_TRUNC]], ptr [[DEST_4]], align 1
+; CHECK-NEXT: store i8 [[ARR_SROA_12_5_EXTRACT_TRUNC]], ptr [[DEST_5]], align 1
+; CHECK-NEXT: store i8 [[ARG]], ptr [[DEST_6]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [8 x i8], align 1
+ %arr.1 = getelementptr inbounds nuw i8, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i8, ptr %arr, i64 2
+ %arr.3 = getelementptr inbounds nuw i8, ptr %arr, i64 3
+ %arr.4 = getelementptr inbounds nuw i8, ptr %arr, i64 4
+ %arr.5 = getelementptr inbounds nuw i8, ptr %arr, i64 5
+ %arr.6 = getelementptr inbounds nuw i8, ptr %arr, i64 6
+ store i8 0, ptr %arr, align 1
+ store i8 1, ptr %arr.1, align 1
+ store i8 2, ptr %arr.2, align 1
+ store i8 3, ptr %arr.3, align 1
+ store i8 4, ptr %arr.4, align 1
+ store i8 5, ptr %arr.5, align 1
+ store i8 6, ptr %arr.6, align 1
+ %move = load i32, ptr %arr.3, align 1
+ store i32 %move, ptr %arr, align 1
+ store i8 %arg, ptr %arr.6, align 1
+ %dest.1 = getelementptr inbounds nuw i8, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i8, ptr %dest, i64 2
+ %dest.3 = getelementptr inbounds nuw i8, ptr %dest, i64 3
+ %dest.4 = getelementptr inbounds nuw i8, ptr %dest, i64 4
+ %dest.5 = getelementptr inbounds nuw i8, ptr %dest, i64 5
+ %dest.6 = getelementptr inbounds nuw i8, ptr %dest, i64 6
+ %val.0 = load i8, ptr %arr, align 1
+ %val.1 = load i8, ptr %arr.1, align 1
+ %val.2 = load i8, ptr %arr.2, align 1
+ %val.3 = load i8, ptr %arr.3, align 1
+ %val.4 = load i8, ptr %arr.4, align 1
+ %val.5 = load i8, ptr %arr.5, align 1
+ %val.6 = load i8, ptr %arr.6, align 1
+ store i8 %val.0, ptr %dest, align 1
+ store i8 %val.1, ptr %dest.1, align 1
+ store i8 %val.2, ptr %dest.2, align 1
+ store i8 %val.3, ptr %dest.3, align 1
+ store i8 %val.4, ptr %dest.4, align 1
+ store i8 %val.5, ptr %dest.5, align 1
+ store i8 %val.6, ptr %dest.6, align 1
+ ret void
+}
+
+; The size of the overlap area equals the size of the non-overlap area
+define void @move_up_equal_overlap_nooverlap(i16 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_up_equal_overlap_nooverlap(
+; CHECK-SAME: i16 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i16, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i16, ptr [[DEST]], i64 2
+; CHECK-NEXT: store i16 [[ARG]], ptr [[DEST]], align 1
+; CHECK-NEXT: store i16 0, ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i16 1, ptr [[DEST_2]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [3 x i16], align 1
+ %arr.1 = getelementptr inbounds nuw i16, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i16, ptr %arr, i64 2
+ store i16 0, ptr %arr, align 1
+ store i16 1, ptr %arr.1, align 1
+ store i16 2, ptr %arr.2, align 1
+ %move = load i32, ptr %arr, align 1
+ store i32 %move, ptr %arr.1, align 1
+ store i16 %arg, ptr %arr, align 1
+ %dest.1 = getelementptr inbounds nuw i16, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i16, ptr %dest, i64 2
+ %val.0 = load i16, ptr %arr, align 1
+ %val.1 = load i16, ptr %arr.1, align 1
+ %val.2 = load i16, ptr %arr.2, align 1
+ store i16 %val.0, ptr %dest, align 1
+ store i16 %val.1, ptr %dest.1, align 1
+ store i16 %val.2, ptr %dest.2, align 1
+ ret void
+}
+
+define void @move_down_equal_overlap_nooverlap(i16 %arg, ptr %dest) {
+; CHECK-LABEL: define void @move_down_equal_overlap_nooverlap(
+; CHECK-SAME: i16 [[ARG:%.*]], ptr [[DEST:%.*]]) {
+; CHECK-NEXT: [[DEST_1:%.*]] = getelementptr inbounds nuw i16, ptr [[DEST]], i64 1
+; CHECK-NEXT: [[DEST_2:%.*]] = getelementptr inbounds nuw i16, ptr [[DEST]], i64 2
+; CHECK-NEXT: store i16 1, ptr [[DEST]], align 1
+; CHECK-NEXT: store i16 2, ptr [[DEST_1]], align 1
+; CHECK-NEXT: store i16 [[ARG]], ptr [[DEST_2]], align 1
+; CHECK-NEXT: ret void
+;
+ %arr = alloca [3 x i16], align 1
+ %arr.1 = getelementptr inbounds nuw i16, ptr %arr, i64 1
+ %arr.2 = getelementptr inbounds nuw i16, ptr %arr, i64 2
+ store i16 0, ptr %arr, align 1
+ store i16 1, ptr %arr.1, align 1
+ store i16 2, ptr %arr.2, align 1
+ %move = load i32, ptr %arr.1, align 1
+ store i32 %move, ptr %arr, align 1
+ store i16 %arg, ptr %arr.2, align 1
+ %dest.1 = getelementptr inbounds nuw i16, ptr %dest, i64 1
+ %dest.2 = getelementptr inbounds nuw i16, ptr %dest, i64 2
+ %val.0 = load i16, ptr %arr, align 1
+ %val.1 = load i16, ptr %arr.1, align 1
+ %val.2 = load i16, ptr %arr.2, align 1
+ store i16 %val.0, ptr %dest, align 1
+ store i16 %val.1, ptr %dest.1, align 1
+ store i16 %val.2, ptr %dest.2, align 1
+ ret void
+}
+
+define void @move_inside_loop(ptr %src, ptr %dest, i64 %n) {
+; CHECK-LABEL: define void @move_inside_loop(
+; CHECK-SAME: ptr [[SRC:%.*]]...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/210061
More information about the llvm-commits
mailing list