Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a45e23b
[ValueTracking] Conservative NoSync check prevents vectorization
kshitijvp May 21, 2026
291b928
Nitpick
kshitijvp May 21, 2026
7ef20bd
Added cleaned Tests
kshitijvp May 21, 2026
8752bd7
Update Test
kshitijvp May 22, 2026
cc94ce5
Nitpick
kshitijvp May 22, 2026
8fd4903
Remove Volatile Check
kshitijvp May 25, 2026
bd85427
Nitpick
kshitijvp May 30, 2026
cb5f6a7
Nitpick
kshitijvp May 30, 2026
615daab
Nitpick
kshitijvp May 30, 2026
a247f2c
Nitpick
kshitijvp May 30, 2026
03fdf50
Modify Test
kshitijvp May 30, 2026
30f80af
Minor Change
kshitijvp May 30, 2026
bcd5a88
Modify Test
kshitijvp May 30, 2026
ce288bd
Nitpick
kshitijvp May 30, 2026
122a413
Nitpick
kshitijvp May 30, 2026
2e56d67
Update Test
kshitijvp May 30, 2026
9a6df94
Merge remote-tracking branch 'origin/main' into issue180180
kshitijvp May 31, 2026
9e04362
nitpick
kshitijvp Feb 13, 2026
bf39dd9
Fixed hasNoSync logic and added negative tests
kshitijvp Feb 13, 2026
b3c08f8
nit
kshitijvp Feb 13, 2026
c56f2ce
Shared isOrderedAtomic function
kshitijvp Feb 14, 2026
4520163
nit
kshitijvp Feb 14, 2026
05197a0
Corrected hasNoSync logic
kshitijvp Feb 14, 2026
d9da6ca
nitpick
kshitijvp Feb 14, 2026
87287cb
Fix FunctionAttrs conflict markers from rebase
kshitijvp May 31, 2026
622601e
Resolve merge conflicts in ValueTracking
kshitijvp May 31, 2026
97183ce
Nitpick
kshitijvp May 31, 2026
fa09e32
Update Test
kshitijvp May 31, 2026
87af6d7
Nitpick
kshitijvp May 31, 2026
425c64e
Modify Tests
kshitijvp Jun 2, 2026
b270ecb
Nitpick
kshitijvp Jun 2, 2026
a135b2a
Merge branch 'main' into issue180180
fhahn Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion llvm/include/llvm/Analysis/ValueTracking.h
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ inline bool isValidAssumeForContext(const Instruction *I,
}

/// Returns true, if no instruction between \p Assume and \p CtxI may free
/// memory and the function is marked as NoSync. The latter ensures the current
/// (including through synchronization). The latter ensures the current
/// function cannot arrange for another thread to free on its behalf.
Comment thread
kshitijvp marked this conversation as resolved.
Outdated
LLVM_ABI bool willNotFreeBetween(const Instruction *Assume,
const Instruction *CtxI);
Expand Down
10 changes: 4 additions & 6 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,18 +708,16 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
for (const auto &[Idx, I] : enumerate(Range)) {
if (Idx > MaxInstrsToCheckForFree)
return false;
if (const auto *CB = dyn_cast<CallBase>(&I))

if (auto *CB = dyn_cast<CallBase>(&I)) {
Comment thread
kshitijvp marked this conversation as resolved.
if (!CB->hasFnAttr(Attribute::NoFree))
return false;
} else if (I.maySynchronize())
return false;
}
return true;
};

// Make sure the current function cannot arrange for another thread to free on
// its behalf.
if (!CtxI->getFunction()->hasNoSync())
return false;

// Handle cross-block case: CtxI in a successor of Assume's block.
const BasicBlock *CtxBB = CtxI->getParent();
const BasicBlock *AssumeBB = Assume->getParent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1456,4 +1456,364 @@ exit:
ret void
}

define i64 @VectorizingWithoutNoSyncAttributeTest(ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) %v, i32 noundef %n) {
; CHECK-LABEL: define i64 @VectorizingWithoutNoSyncAttributeTest(
; CHECK-SAME: ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) [[V:%.*]], i32 noundef [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[L_V:%.*]] = load ptr, ptr [[V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_V:%.*]] = ptrtoint ptr [[L_V]] to i64
; CHECK-NEXT: [[GEP_V:%.*]] = getelementptr inbounds nuw i8, ptr [[V]], i64 8
; CHECK-NEXT: [[L_GEP_V:%.*]] = load ptr, ptr [[GEP_V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_GEP_V:%.*]] = ptrtoint ptr [[L_GEP_V]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[PI_L_GEP_V]], [[PI_L_V]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[L_V]], i64 [[SUB]]) ]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[L_V]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_1]], [[EXIT:label %.*]], label %[[LOOP_PREHEADER:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PI_L_GEP_V]], -4
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 [[TMP0]], [[PI_L_V]]
; CHECK-NEXT: [[TMP2:%.*]] = lshr i64 [[TMP1]], 2
; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 1
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[TMP3]], 2
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i64 [[TMP3]], 2
; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[TMP3]], [[N_MOD_VF]]
; CHECK-NEXT: [[TMP4:%.*]] = shl i64 [[N_VEC]], 2
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP4]]
; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <2 x i32> poison, i32 [[N]], i64 0
; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <2 x i32> [[BROADCAST_SPLATINSERT]], <2 x i32> poison, <2 x i32> zeroinitializer
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
; CHECK-NEXT: [[TMP6:%.*]] = shl i64 [[INDEX]], 2
; CHECK-NEXT: [[NEXT_GEP:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP6]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[NEXT_GEP]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp slt <2 x i32> [[WIDE_LOAD]], [[BROADCAST_SPLAT]]
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
; CHECK-NEXT: [[TMP8:%.*]] = freeze <2 x i1> [[TMP7]]
; CHECK-NEXT: [[C_2:%.*]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[TMP8]])
; CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
; CHECK-NEXT: br i1 [[C_2]], label %[[LOOP_EARLY_EXIT:.*]], label %[[LOOP_LATCH]]
; CHECK: [[LOOP_LATCH]]:
; CHECK-NEXT: br i1 [[TMP10]], label %[[LOOP_COMP:.*]], label %[[LOOP_HEADER]], !llvm.loop [[LOOP24:![0-9]+]]
; CHECK: [[LOOP_COMP]]:
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[TMP3]], [[N_VEC]]
; CHECK-NEXT: br i1 [[CMP_N]], [[LOOP_COMP1:label %.*]], label %[[SCALAR_PH]]
; CHECK: [[LOOP_EARLY_EXIT]]:
; CHECK-NEXT: [[TMP11:%.*]] = call i64 @llvm.experimental.cttz.elts.i64.v2i1(<2 x i1> [[TMP7]], i1 false)
; CHECK-NEXT: [[TMP12:%.*]] = add i64 [[INDEX]], [[TMP11]]
; CHECK-NEXT: [[TMP13:%.*]] = shl i64 [[TMP12]], 2
; CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP13]]
; CHECK-NEXT: br [[LOOP_EARLY_EXIT1:label %.*]]
; CHECK: [[SCALAR_PH]]:
;
entry:
%l.v = load ptr, ptr %v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
%pi.l.v = ptrtoint ptr %l.v to i64
%gep.v = getelementptr inbounds nuw i8, ptr %v, i64 8
%l.gep.v = load ptr, ptr %gep.v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%pi.l.gep.v = ptrtoint ptr %l.gep.v to i64
%sub = sub i64 %pi.l.gep.v, %pi.l.v
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %l.v, i64 %sub) ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file focuses on testing deref assumptions with constant sizes. With non-constant expressions should go to llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-variable-size.ll

But the interesting aspect of the new tests are the nosync etc checks. so I think you can just remove the ptrtoint/sub code to compute the deref size. Similarly, you can just pass l.v and l.gep.v as pointer arguments without loss of generality.

@kshitijvp kshitijvp May 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On removing those ptrtoint/sub the Tests which should be vectorized are not getting vectorized.
I will just move the tests to
llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-variable-size.ll

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but I think it should be possible to take one of the existing simpler tests that do not get vectorized from llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll like the changed deref_assumption_in_header_constant_trip_count_nofree_via_context_b and use that to test the main logic skipping with various instructions before the assumption? That way the entry block would be a single deref assumption + the instruction we are testing.

The existing positive test with the early exit could go llvm/test/Transforms/LoopVectorize/single-early-exit-deref-assumptions.ll, I forgot that there was a dedicated file for reasoning about early exits

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fhahn Isn't deref_assumption_in_header_constant_trip_count_nofree_via_context_but_missing_nosync getting vectorized too?

The Check-Lines contain the Vector-Preheader and Vector-Body which means it is getting vectorized.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted rewrote it to not get vectorized by creating a new point %a.1 which loads from %a.
And an atomic instruction that works on %a next to the dereferenceable instruction. The Loop Ends up using that %a.

However still that is getting vectorized.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the tests within that file are getting vectorized.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, what I meant is take deref_assumption_in_header_constant_trip_count_nofree_via_context_but_missing_nosync and add the volatile loads etc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

define void @atomicNotVectorizingTest(ptr noalias noundef %a, ptr noalias %b, ptr noalias %c) {
; CHECK-LABEL: define void @atomicNotVectorizingTest(
; CHECK-SAME: ptr noalias noundef [[A:%.*]], ptr noalias [[B:%.*]], ptr noalias [[C:%.*]]) {
; CHECK-NEXT:  [[ENTRY:.*:]]
; CHECK-NEXT:    [[A_1:%.*]] = load ptr, ptr [[A]], align 8
; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[A_1]], i64 4), "dereferenceable"(ptr [[A_1]], i64 4000) ]
; CHECK-NEXT:    [[ATOMIC:%.*]] = load atomic ptr, ptr [[A]] seq_cst, align 8
; CHECK-NEXT:    br label %[[VECTOR_PH:.*]]
; CHECK:       [[VECTOR_PH]]:
; CHECK-NEXT:    br label %[[PRED_LOAD_IF:.*]]
; CHECK:       [[PRED_LOAD_IF]]:
; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[PRED_LOAD_IF5:.*]] ]
; CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP5]], align 4
; CHECK-NEXT:    [[TMP4:%.*]] = icmp slt <2 x i32> [[WIDE_LOAD]], zeroinitializer
; CHECK-NEXT:    [[TMP10:%.*]] = extractelement <2 x i1> [[TMP4]], i64 0
; CHECK-NEXT:    br i1 [[TMP10]], label %[[PRED_LOAD_IF1:.*]], label %[[PRED_LOAD_CONTINUE:.*]]
; CHECK:       [[PRED_LOAD_IF1]]:
; CHECK-NEXT:    [[TMP6:%.*]] = getelementptr i32, ptr [[A_1]], i64 [[INDEX]]
; CHECK-NEXT:    [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4
; CHECK-NEXT:    [[TMP8:%.*]] = insertelement <2 x i32> poison, i32 [[TMP7]], i64 0
; CHECK-NEXT:    br label %[[PRED_LOAD_CONTINUE]]
; CHECK:       [[PRED_LOAD_CONTINUE]]:
; CHECK-NEXT:    [[TMP9:%.*]] = phi <2 x i32> [ poison, %[[PRED_LOAD_IF]] ], [ [[TMP8]], %[[PRED_LOAD_IF1]] ]
; CHECK-NEXT:    [[C_1:%.*]] = extractelement <2 x i1> [[TMP4]], i64 1
; CHECK-NEXT:    br i1 [[C_1]], label %[[PRED_LOAD_CONTINUE6:.*]], label %[[PRED_LOAD_IF5]]
; CHECK:       [[PRED_LOAD_CONTINUE6]]:
; CHECK-NEXT:    [[TMP13:%.*]] = add i64 [[INDEX]], 1
; CHECK-NEXT:    [[TMP11:%.*]] = getelementptr i32, ptr [[A_1]], i64 [[TMP13]]
; CHECK-NEXT:    [[TMP12:%.*]] = load i32, ptr [[TMP11]], align 4
; CHECK-NEXT:    [[TMP14:%.*]] = insertelement <2 x i32> [[TMP9]], i32 [[TMP12]], i64 1
; CHECK-NEXT:    br label %[[PRED_LOAD_IF5]]
; CHECK:       [[PRED_LOAD_IF5]]:
; CHECK-NEXT:    [[TMP16:%.*]] = phi <2 x i32> [ [[TMP9]], %[[PRED_LOAD_CONTINUE]] ], [ [[TMP14]], %[[PRED_LOAD_CONTINUE6]] ]
; CHECK-NEXT:    [[PREDPHI:%.*]] = select <2 x i1> [[TMP4]], <2 x i32> [[TMP16]], <2 x i32> [[WIDE_LOAD]]
; CHECK-NEXT:    [[TMP15:%.*]] = getelementptr inbounds i32, ptr [[C]], i64 [[INDEX]]
; CHECK-NEXT:    store <2 x i32> [[PREDPHI]], ptr [[TMP15]], align 4
; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
; CHECK-NEXT:    [[TMP17:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1000
; CHECK-NEXT:    br i1 [[TMP17]], label %[[MIDDLE_BLOCK:.*]], label %[[PRED_LOAD_IF]], !llvm.loop [[LOOP24:![0-9]+]]
; CHECK:       [[MIDDLE_BLOCK]]:
; CHECK-NEXT:    br label %[[EXIT:.*]]
; CHECK:       [[EXIT]]:
; CHECK-NEXT:    ret void
;
entry:
  %a.1 = load ptr, ptr %a, align 8
  call void @llvm.assume(i1 true) [ "align"(ptr %a.1, i64 4), "dereferenceable"(ptr %a.1, i64 4000) ]
  %atomic = load atomic ptr, ptr %a seq_cst, align 8                      ; Atomic Instruction
  br label %loop.header

loop.header:
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %gep.a = getelementptr i32, ptr %a.1, i64 %iv
  %gep.b = getelementptr inbounds i32, ptr %b, i64 %iv
  %l.b = load i32, ptr %gep.b, align 4
  %c.1 = icmp sge i32 %l.b, 0
  br i1 %c.1, label %loop.latch, label %loop.then

loop.then:
  %l.a = load i32, ptr %gep.a, align 4
  br label %loop.latch

loop.latch:
  %merge = phi i32 [ %l.a, %loop.then ], [ %l.b, %loop.header ]
  %gep.c = getelementptr inbounds i32, ptr %c, i64 %iv
  store i32 %merge, ptr %gep.c, align 4
  %iv.next = add nuw nsw i64 %iv, 1
  %ec = icmp eq i64 %iv.next, 1000
  br i1 %ec, label %exit, label %loop.header

exit:
  ret void
}

Here is my modified test. I have added the Atomic Instruction after the Dereferenceable Asssume Instruction. However still this is getting Vectorized...

Putting the Atomic Instruction inside the Loop Header stops vectorization but that's not what we want to test right?

@fhahn fhahn Jun 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it vectorizes, but does not execute the load in loop.then unconditionally. If the deref assumption can be used, it will execute the load unconditionally.

So with and without the load atomic it will get vectorized, but differently

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made the changes.

call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
Comment thread
kshitijvp marked this conversation as resolved.
Outdated
%c.1 = icmp eq ptr %l.v, %l.gep.v
br i1 %c.1, label %exit, label %loop.preheader

loop.preheader:
br label %loop.header

loop.header:
%merge = phi ptr [ %gep.merge, %loop.latch ], [ %l.v, %loop.preheader ]
Comment thread
kshitijvp marked this conversation as resolved.
Outdated
%val = load i32, ptr %merge, align 4
%c.2 = icmp slt i32 %val, %n
br i1 %c.2, label %loop.early.exit, label %loop.latch

loop.early.exit:
%iv = phi ptr [ %merge, %loop.header ]
%pi.iv = ptrtoint ptr %iv to i64
Comment thread
kshitijvp marked this conversation as resolved.
Outdated
br label %exit

loop.latch:
%gep.merge = getelementptr inbounds nuw i8, ptr %merge, i64 4
%c.3 = icmp eq ptr %gep.merge, %l.gep.v
br i1 %c.3, label %loop.comp, label %loop.header

loop.comp:
br label %exit

exit:
%mer = phi i64 [ %pi.l.gep.v, %entry ], [ %pi.iv, %loop.early.exit ], [ %pi.l.gep.v, %loop.comp ]
ret i64 %mer
}

define i64 @volatileVectorizingTest(ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) %v, i32 noundef %n) {
; CHECK-LABEL: define i64 @volatileVectorizingTest(
; CHECK-SAME: ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) [[V:%.*]], i32 noundef [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[L_V:%.*]] = load ptr, ptr [[V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_V:%.*]] = ptrtoint ptr [[L_V]] to i64
; CHECK-NEXT: [[GEP_V:%.*]] = getelementptr inbounds nuw i8, ptr [[V]], i64 8
; CHECK-NEXT: [[L_GEP_V:%.*]] = load ptr, ptr [[GEP_V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_GEP_V:%.*]] = ptrtoint ptr [[L_GEP_V]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[PI_L_GEP_V]], [[PI_L_V]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[L_V]], i64 [[SUB]]) ]
; CHECK-NEXT: [[VOLATILE:%.*]] = load volatile ptr, ptr [[V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[L_V]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_1]], [[EXIT:label %.*]], label %[[LOOP_PREHEADER:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PI_L_GEP_V]], -4
; CHECK-NEXT: [[TMP1:%.*]] = sub i64 [[TMP0]], [[PI_L_V]]
; CHECK-NEXT: [[TMP2:%.*]] = lshr i64 [[TMP1]], 2
; CHECK-NEXT: [[TMP3:%.*]] = add nuw nsw i64 [[TMP2]], 1
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[TMP3]], 2
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label %[[SCALAR_PH:.*]], label %[[VECTOR_PH:.*]]
; CHECK: [[VECTOR_PH]]:
; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i64 [[TMP3]], 2
; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 [[TMP3]], [[N_MOD_VF]]
; CHECK-NEXT: [[TMP4:%.*]] = shl i64 [[N_VEC]], 2
; CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP4]]
; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <2 x i32> poison, i32 [[N]], i64 0
; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <2 x i32> [[BROADCAST_SPLATINSERT]], <2 x i32> poison, <2 x i32> zeroinitializer
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
; CHECK: [[VECTOR_BODY]]:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY_INTERIM:.*]] ]
; CHECK-NEXT: [[TMP6:%.*]] = shl i64 [[INDEX]], 2
; CHECK-NEXT: [[NEXT_GEP:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP6]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[NEXT_GEP]], align 4
; CHECK-NEXT: [[TMP7:%.*]] = icmp slt <2 x i32> [[WIDE_LOAD]], [[BROADCAST_SPLAT]]
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
; CHECK-NEXT: [[TMP8:%.*]] = freeze <2 x i1> [[TMP7]]
; CHECK-NEXT: [[TMP9:%.*]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[TMP8]])
; CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
; CHECK-NEXT: br i1 [[TMP9]], label %[[VECTOR_EARLY_EXIT:.*]], label %[[VECTOR_BODY_INTERIM]]
; CHECK: [[VECTOR_BODY_INTERIM]]:
; CHECK-NEXT: br i1 [[TMP10]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP26:![0-9]+]]
; CHECK: [[MIDDLE_BLOCK]]:
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[TMP3]], [[N_VEC]]
; CHECK-NEXT: br i1 [[CMP_N]], [[LOOP_COMP:label %.*]], label %[[SCALAR_PH]]
; CHECK: [[VECTOR_EARLY_EXIT]]:
; CHECK-NEXT: [[TMP11:%.*]] = call i64 @llvm.experimental.cttz.elts.i64.v2i1(<2 x i1> [[TMP7]], i1 false)
; CHECK-NEXT: [[TMP12:%.*]] = add i64 [[INDEX]], [[TMP11]]
; CHECK-NEXT: [[TMP13:%.*]] = shl i64 [[TMP12]], 2
; CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[L_V]], i64 [[TMP13]]
; CHECK-NEXT: br [[LOOP_EARLY_EXIT:label %.*]]
; CHECK: [[SCALAR_PH]]:
;
entry:
%l.v = load ptr, ptr %v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
%pi.l.v = ptrtoint ptr %l.v to i64
%gep.v = getelementptr inbounds nuw i8, ptr %v, i64 8
%l.gep.v = load ptr, ptr %gep.v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%pi.l.gep.v = ptrtoint ptr %l.gep.v to i64
%sub = sub i64 %pi.l.gep.v, %pi.l.v
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %l.v, i64 %sub) ]
%volatile = load volatile ptr, ptr %v, align 8 ; Volatile Instruction
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%c.1 = icmp eq ptr %l.v, %l.gep.v
br i1 %c.1, label %exit, label %loop.preheader

loop.preheader:
br label %loop.header

loop.header:
%merge = phi ptr [ %gep.merge, %loop.latch ], [ %l.v, %loop.preheader ]
%val = load i32, ptr %merge, align 4
%c.2 = icmp slt i32 %val, %n
br i1 %c.2, label %loop.early.exit, label %loop.latch

loop.early.exit:
%iv = phi ptr [ %merge, %loop.header ]
%pi.iv = ptrtoint ptr %iv to i64
br label %exit

loop.latch:
%gep.merge = getelementptr inbounds nuw i8, ptr %merge, i64 4
%c.3 = icmp eq ptr %gep.merge, %l.gep.v
br i1 %c.3, label %loop.comp, label %loop.header

loop.comp:
br label %exit

exit:
%mer = phi i64 [ %pi.l.gep.v, %entry ], [ %pi.iv, %loop.early.exit ], [ %pi.l.gep.v, %loop.comp ]
ret i64 %mer
}

define i64 @fenceNotVectorizingTest(ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) %v, i32 noundef %n) {
; CHECK-LABEL: define i64 @fenceNotVectorizingTest(
; CHECK-SAME: ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) [[V:%.*]], i32 noundef [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[L_V:%.*]] = load ptr, ptr [[V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_V:%.*]] = ptrtoint ptr [[L_V]] to i64
; CHECK-NEXT: [[GEP_V:%.*]] = getelementptr inbounds nuw i8, ptr [[V]], i64 8
; CHECK-NEXT: [[L_GEP_V:%.*]] = load ptr, ptr [[GEP_V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_GEP_V:%.*]] = ptrtoint ptr [[L_GEP_V]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[PI_L_GEP_V]], [[PI_L_V]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[L_V]], i64 [[SUB]]) ]
; CHECK-NEXT: fence seq_cst
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[L_V]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_1]], label %[[EXIT:.*]], label %[[LOOP_PREHEADER:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[MERGE:%.*]] = phi ptr [ [[GEP_MERGE:%.*]], %[[LOOP_LATCH:.*]] ], [ [[L_V]], %[[LOOP_PREHEADER]] ]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[MERGE]], align 4
; CHECK-NEXT: [[C_2:%.*]] = icmp slt i32 [[VAL]], [[N]]
; CHECK-NEXT: br i1 [[C_2]], label %[[LOOP_EARLY_EXIT:.*]], label %[[LOOP_LATCH]]
; CHECK: [[LOOP_EARLY_EXIT]]:
; CHECK-NEXT: [[IV:%.*]] = phi ptr [ [[MERGE]], %[[LOOP_HEADER]] ]
; CHECK-NEXT: [[PI_IV:%.*]] = ptrtoint ptr [[IV]] to i64
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[LOOP_LATCH]]:
; CHECK-NEXT: [[GEP_MERGE]] = getelementptr inbounds nuw i8, ptr [[MERGE]], i64 4
; CHECK-NEXT: [[C_3:%.*]] = icmp eq ptr [[GEP_MERGE]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_3]], label %[[LOOP_COMP:.*]], label %[[LOOP_HEADER]]
; CHECK: [[LOOP_COMP]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[MER:%.*]] = phi i64 [ [[PI_L_GEP_V]], %[[ENTRY]] ], [ [[PI_IV]], %[[LOOP_EARLY_EXIT]] ], [ [[PI_L_GEP_V]], %[[LOOP_COMP]] ]
; CHECK-NEXT: ret i64 [[MER]]
;
entry:
%l.v = load ptr, ptr %v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
%pi.l.v = ptrtoint ptr %l.v to i64
%gep.v = getelementptr inbounds nuw i8, ptr %v, i64 8
%l.gep.v = load ptr, ptr %gep.v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%pi.l.gep.v = ptrtoint ptr %l.gep.v to i64
%sub = sub i64 %pi.l.gep.v, %pi.l.v
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %l.v, i64 %sub) ]
fence seq_cst ; Fence Instruction
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%c.1 = icmp eq ptr %l.v, %l.gep.v
br i1 %c.1, label %exit, label %loop.preheader

loop.preheader:
br label %loop.header

loop.header:
%merge = phi ptr [ %gep.merge, %loop.latch ], [ %l.v, %loop.preheader ]
%val = load i32, ptr %merge, align 4
%c.2 = icmp slt i32 %val, %n
br i1 %c.2, label %loop.early.exit, label %loop.latch

loop.early.exit:
%iv = phi ptr [ %merge, %loop.header ]
%pi.iv = ptrtoint ptr %iv to i64
br label %exit

loop.latch:
%gep.merge = getelementptr inbounds nuw i8, ptr %merge, i64 4
%c.3 = icmp eq ptr %gep.merge, %l.gep.v
br i1 %c.3, label %loop.comp, label %loop.header

loop.comp:
br label %exit

exit:
%mer = phi i64 [ %pi.l.gep.v, %entry ], [ %pi.iv, %loop.early.exit ], [ %pi.l.gep.v, %loop.comp ]
ret i64 %mer
}

define i64 @atomicNotVectorizingTest(ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) %v, i32 noundef %n) {
; CHECK-LABEL: define i64 @atomicNotVectorizingTest(
; CHECK-SAME: ptr noundef nonnull readonly align 8 captures(none) dereferenceable(24) [[V:%.*]], i32 noundef [[N:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: [[L_V:%.*]] = load ptr, ptr [[V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_V:%.*]] = ptrtoint ptr [[L_V]] to i64
; CHECK-NEXT: [[GEP_V:%.*]] = getelementptr inbounds nuw i8, ptr [[V]], i64 8
; CHECK-NEXT: [[L_GEP_V:%.*]] = load ptr, ptr [[GEP_V]], align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[PI_L_GEP_V:%.*]] = ptrtoint ptr [[L_GEP_V]] to i64
; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[PI_L_GEP_V]], [[PI_L_V]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[L_V]], i64 [[SUB]]) ]
; CHECK-NEXT: [[ATOMIC:%.*]] = load atomic ptr, ptr [[V]] seq_cst, align 8
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_V]], i64 4) ]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[L_GEP_V]], i64 4) ]
; CHECK-NEXT: [[C_1:%.*]] = icmp eq ptr [[L_V]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_1]], label %[[EXIT:.*]], label %[[LOOP_PREHEADER:.*]]
; CHECK: [[LOOP_PREHEADER]]:
; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
; CHECK: [[LOOP_HEADER]]:
; CHECK-NEXT: [[MERGE:%.*]] = phi ptr [ [[GEP_MERGE:%.*]], %[[LOOP_LATCH:.*]] ], [ [[L_V]], %[[LOOP_PREHEADER]] ]
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[MERGE]], align 4
; CHECK-NEXT: [[C_2:%.*]] = icmp slt i32 [[VAL]], [[N]]
; CHECK-NEXT: br i1 [[C_2]], label %[[LOOP_EARLY_EXIT:.*]], label %[[LOOP_LATCH]]
; CHECK: [[LOOP_EARLY_EXIT]]:
; CHECK-NEXT: [[IV:%.*]] = phi ptr [ [[MERGE]], %[[LOOP_HEADER]] ]
; CHECK-NEXT: [[PI_IV:%.*]] = ptrtoint ptr [[IV]] to i64
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[LOOP_LATCH]]:
; CHECK-NEXT: [[GEP_MERGE]] = getelementptr inbounds nuw i8, ptr [[MERGE]], i64 4
; CHECK-NEXT: [[C_3:%.*]] = icmp eq ptr [[GEP_MERGE]], [[L_GEP_V]]
; CHECK-NEXT: br i1 [[C_3]], label %[[LOOP_COMP:.*]], label %[[LOOP_HEADER]]
; CHECK: [[LOOP_COMP]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[MER:%.*]] = phi i64 [ [[PI_L_GEP_V]], %[[ENTRY]] ], [ [[PI_IV]], %[[LOOP_EARLY_EXIT]] ], [ [[PI_L_GEP_V]], %[[LOOP_COMP]] ]
; CHECK-NEXT: ret i64 [[MER]]
;
entry:
%l.v = load ptr, ptr %v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
%pi.l.v = ptrtoint ptr %l.v to i64
%gep.v = getelementptr inbounds nuw i8, ptr %v, i64 8
%l.gep.v = load ptr, ptr %gep.v, align 8
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%pi.l.gep.v = ptrtoint ptr %l.gep.v to i64
%sub = sub i64 %pi.l.gep.v, %pi.l.v
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %l.v, i64 %sub) ]
%atomic = load atomic ptr, ptr %v seq_cst, align 8 ; Atomic Instruction
call void @llvm.assume(i1 true) [ "align"(ptr %l.v, i64 4) ]
call void @llvm.assume(i1 true) [ "align"(ptr %l.gep.v, i64 4) ]
%c.1 = icmp eq ptr %l.v, %l.gep.v
br i1 %c.1, label %exit, label %loop.preheader

loop.preheader:
br label %loop.header

loop.header:
%merge = phi ptr [ %gep.merge, %loop.latch ], [ %l.v, %loop.preheader ]
%val = load i32, ptr %merge, align 4
%c.2 = icmp slt i32 %val, %n
br i1 %c.2, label %loop.early.exit, label %loop.latch

loop.early.exit:
%iv = phi ptr [ %merge, %loop.header ]
%pi.iv = ptrtoint ptr %iv to i64
br label %exit

loop.latch:
%gep.merge = getelementptr inbounds nuw i8, ptr %merge, i64 4
%c.3 = icmp eq ptr %gep.merge, %l.gep.v
br i1 %c.3, label %loop.comp, label %loop.header

loop.comp:
br label %exit

exit:
%mer = phi i64 [ %pi.l.gep.v, %entry ], [ %pi.iv, %loop.early.exit ], [ %pi.l.gep.v, %loop.comp ]
ret i64 %mer
}
Loading
Loading