-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ValueTracking: introduce llvm::isNotCrossLaneOperation #112011
Conversation
Factor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isLanewiseOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/r9jCpP
@llvm/pr-subscribers-llvm-analysis Author: Ramkumar Ramachandra (artagnon) ChangesFactor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isLanewiseOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/r9jCpP Full diff: https://github.com/llvm/llvm-project/pull/112011.diff 7 Files Affected:
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 5749a34d511dd7..780b88107fd67b 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -791,6 +791,10 @@ bool onlyUsedByLifetimeMarkers(const Value *V);
/// droppable instructions.
bool onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V);
+/// Return true if the instruction is known to be a vector lane-wise operation
+/// i.e. if it doesn't potentially cross vector lanes.
+bool isLanewiseOperation(const Instruction *I);
+
/// Return true if the instruction does not have any effects besides
/// calculating the result and does not have undefined behavior.
///
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 90f05d43a2b147..c84292fb30cd4d 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4343,13 +4343,11 @@ static Value *simplifyWithOpReplaced(Value *V, Value *Op, Value *RepOp,
if (isa<PHINode>(I))
return nullptr;
- if (Op->getType()->isVectorTy()) {
+ if (Op->getType()->isVectorTy() &&
+ (!I->getType()->isVectorTy() || !isLanewiseOperation(I)))
// For vector types, the simplification must hold per-lane, so forbid
// potentially cross-lane operations like shufflevector.
- if (!I->getType()->isVectorTy() || isa<ShuffleVectorInst>(I) ||
- isa<CallBase>(I) || isa<BitCastInst>(I))
- return nullptr;
- }
+ return nullptr;
// Don't fold away llvm.is.constant checks based on assumptions.
if (match(I, m_Intrinsic<Intrinsic::is_constant>()))
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 446ff42f3e243d..284123777a5350 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6947,6 +6947,30 @@ bool llvm::onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V) {
V, /* AllowLifetime */ true, /* AllowDroppable */ true);
}
+bool llvm::isLanewiseOperation(const Instruction *I) {
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::ctlz:
+ case Intrinsic::cttz:
+ case Intrinsic::ctpop:
+ case Intrinsic::umin:
+ case Intrinsic::umax:
+ case Intrinsic::smin:
+ case Intrinsic::smax:
+ case Intrinsic::usub_sat:
+ case Intrinsic::uadd_sat:
+ case Intrinsic::ssub_sat:
+ case Intrinsic::sadd_sat:
+ return true;
+ default:
+ return false;
+ }
+ }
+ auto *Shuffle = dyn_cast<ShuffleVectorInst>(I);
+ return (!Shuffle || Shuffle->isIdentity() || Shuffle->isSelect()) &&
+ !isa<CallBase>(I) && !isa<BitCastInst>(I);
+}
+
bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst,
const Instruction *CtxI,
AssumptionCache *AC,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index f7a9406791801c..be9d3e3abd2df5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3629,26 +3629,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
// * The intrinsic is speculatable.
// * The select condition is not a vector, or the intrinsic does not
// perform cross-lane operations.
- switch (IID) {
- case Intrinsic::ctlz:
- case Intrinsic::cttz:
- case Intrinsic::ctpop:
- case Intrinsic::umin:
- case Intrinsic::umax:
- case Intrinsic::smin:
- case Intrinsic::smax:
- case Intrinsic::usub_sat:
- case Intrinsic::uadd_sat:
- case Intrinsic::ssub_sat:
- case Intrinsic::sadd_sat:
+ if (isLanewiseOperation(II))
for (Value *Op : II->args())
if (auto *Sel = dyn_cast<SelectInst>(Op))
if (Instruction *R = FoldOpIntoSelect(*II, Sel))
return R;
- [[fallthrough]];
- default:
- break;
- }
if (Instruction *Shuf = foldShuffledIntrinsicOperands(II, Builder))
return Shuf;
diff --git a/llvm/test/Transforms/InstCombine/ispow2.ll b/llvm/test/Transforms/InstCombine/ispow2.ll
index c21ad95f83a1c4..2bc629a5cddf24 100644
--- a/llvm/test/Transforms/InstCombine/ispow2.ll
+++ b/llvm/test/Transforms/InstCombine/ispow2.ll
@@ -977,11 +977,7 @@ define i1 @is_pow2or0_ctpop_wrong_pred2_logical(i32 %x) {
define <2 x i1> @is_pow2or0_ctpop_commute_vec_wrong_pred3(<2 x i8> %x) {
; CHECK-LABEL: @is_pow2or0_ctpop_commute_vec_wrong_pred3(
-; CHECK-NEXT: [[T0:%.*]] = tail call range(i8 0, 9) <2 x i8> @llvm.ctpop.v2i8(<2 x i8> [[X:%.*]])
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[T0]], <i8 1, i8 1>
-; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT: [[R:%.*]] = and <2 x i1> [[CMP]], [[ISZERO]]
-; CHECK-NEXT: ret <2 x i1> [[R]]
+; CHECK-NEXT: ret <2 x i1> zeroinitializer
;
%t0 = tail call <2 x i8> @llvm.ctpop.v2i8(<2 x i8> %x)
%cmp = icmp eq <2 x i8> %t0, <i8 1, i8 1>
@@ -1174,11 +1170,7 @@ define i1 @isnot_pow2nor0_ctpop_wrong_pred2_logical(i32 %x) {
define <2 x i1> @isnot_pow2nor0_wrong_pred3_ctpop_commute_vec(<2 x i8> %x) {
; CHECK-LABEL: @isnot_pow2nor0_wrong_pred3_ctpop_commute_vec(
-; CHECK-NEXT: [[T0:%.*]] = tail call range(i8 0, 9) <2 x i8> @llvm.ctpop.v2i8(<2 x i8> [[X:%.*]])
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[T0]], <i8 1, i8 1>
-; CHECK-NEXT: [[NOTZERO:%.*]] = icmp ne <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT: [[R:%.*]] = or <2 x i1> [[CMP]], [[NOTZERO]]
-; CHECK-NEXT: ret <2 x i1> [[R]]
+; CHECK-NEXT: ret <2 x i1> <i1 true, i1 true>
;
%t0 = tail call <2 x i8> @llvm.ctpop.v2i8(<2 x i8> %x)
%cmp = icmp ne <2 x i8> %t0, <i8 1, i8 1>
diff --git a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
index cc8f5d53fddddd..2e6fe26c03051b 100644
--- a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
+++ b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
@@ -208,10 +208,8 @@ define <2 x i32> @select_clz_to_ctz_vec_with_undef(<2 x i32> %a) {
; CHECK-NEXT: [[SUB:%.*]] = sub <2 x i32> zeroinitializer, [[A:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <2 x i32> [[A]], [[SUB]]
; CHECK-NEXT: [[LZ:%.*]] = tail call range(i32 0, 33) <2 x i32> @llvm.ctlz.v2i32(<2 x i32> [[AND]], i1 true)
-; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq <2 x i32> [[A]], zeroinitializer
; CHECK-NEXT: [[SUB1:%.*]] = xor <2 x i32> [[LZ]], <i32 31, i32 undef>
-; CHECK-NEXT: [[COND:%.*]] = select <2 x i1> [[TOBOOL]], <2 x i32> [[LZ]], <2 x i32> [[SUB1]]
-; CHECK-NEXT: ret <2 x i32> [[COND]]
+; CHECK-NEXT: ret <2 x i32> [[SUB1]]
;
%sub = sub <2 x i32> zeroinitializer, %a
%and = and <2 x i32> %sub, %a
diff --git a/llvm/test/Transforms/InstSimplify/select.ll b/llvm/test/Transforms/InstSimplify/select.ll
index 1e503afae1a69d..4618e1143dc8f9 100644
--- a/llvm/test/Transforms/InstSimplify/select.ll
+++ b/llvm/test/Transforms/InstSimplify/select.ll
@@ -1087,13 +1087,10 @@ define i32 @select_ctpop_zero(i32 %x) {
ret i32 %sel
}
-; FIXME: This is safe to fold.
define <2 x i32> @select_ctpop_zero_vec(<2 x i32> %x) {
; CHECK-LABEL: @select_ctpop_zero_vec(
-; CHECK-NEXT: [[T0:%.*]] = icmp eq <2 x i32> [[X:%.*]], zeroinitializer
-; CHECK-NEXT: [[T1:%.*]] = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> [[X]])
-; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[T0]], <2 x i32> zeroinitializer, <2 x i32> [[T1]]
-; CHECK-NEXT: ret <2 x i32> [[SEL]]
+; CHECK-NEXT: [[T1:%.*]] = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> [[X:%.*]])
+; CHECK-NEXT: ret <2 x i32> [[T1]]
;
%t0 = icmp eq <2 x i32> %x, zeroinitializer
%t1 = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> %x)
|
@llvm/pr-subscribers-llvm-transforms Author: Ramkumar Ramachandra (artagnon) ChangesFactor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isLanewiseOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/r9jCpP Full diff: https://github.com/llvm/llvm-project/pull/112011.diff 7 Files Affected:
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 5749a34d511dd7..780b88107fd67b 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -791,6 +791,10 @@ bool onlyUsedByLifetimeMarkers(const Value *V);
/// droppable instructions.
bool onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V);
+/// Return true if the instruction is known to be a vector lane-wise operation
+/// i.e. if it doesn't potentially cross vector lanes.
+bool isLanewiseOperation(const Instruction *I);
+
/// Return true if the instruction does not have any effects besides
/// calculating the result and does not have undefined behavior.
///
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 90f05d43a2b147..c84292fb30cd4d 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4343,13 +4343,11 @@ static Value *simplifyWithOpReplaced(Value *V, Value *Op, Value *RepOp,
if (isa<PHINode>(I))
return nullptr;
- if (Op->getType()->isVectorTy()) {
+ if (Op->getType()->isVectorTy() &&
+ (!I->getType()->isVectorTy() || !isLanewiseOperation(I)))
// For vector types, the simplification must hold per-lane, so forbid
// potentially cross-lane operations like shufflevector.
- if (!I->getType()->isVectorTy() || isa<ShuffleVectorInst>(I) ||
- isa<CallBase>(I) || isa<BitCastInst>(I))
- return nullptr;
- }
+ return nullptr;
// Don't fold away llvm.is.constant checks based on assumptions.
if (match(I, m_Intrinsic<Intrinsic::is_constant>()))
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 446ff42f3e243d..284123777a5350 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6947,6 +6947,30 @@ bool llvm::onlyUsedByLifetimeMarkersOrDroppableInsts(const Value *V) {
V, /* AllowLifetime */ true, /* AllowDroppable */ true);
}
+bool llvm::isLanewiseOperation(const Instruction *I) {
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::ctlz:
+ case Intrinsic::cttz:
+ case Intrinsic::ctpop:
+ case Intrinsic::umin:
+ case Intrinsic::umax:
+ case Intrinsic::smin:
+ case Intrinsic::smax:
+ case Intrinsic::usub_sat:
+ case Intrinsic::uadd_sat:
+ case Intrinsic::ssub_sat:
+ case Intrinsic::sadd_sat:
+ return true;
+ default:
+ return false;
+ }
+ }
+ auto *Shuffle = dyn_cast<ShuffleVectorInst>(I);
+ return (!Shuffle || Shuffle->isIdentity() || Shuffle->isSelect()) &&
+ !isa<CallBase>(I) && !isa<BitCastInst>(I);
+}
+
bool llvm::isSafeToSpeculativelyExecute(const Instruction *Inst,
const Instruction *CtxI,
AssumptionCache *AC,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index f7a9406791801c..be9d3e3abd2df5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3629,26 +3629,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
// * The intrinsic is speculatable.
// * The select condition is not a vector, or the intrinsic does not
// perform cross-lane operations.
- switch (IID) {
- case Intrinsic::ctlz:
- case Intrinsic::cttz:
- case Intrinsic::ctpop:
- case Intrinsic::umin:
- case Intrinsic::umax:
- case Intrinsic::smin:
- case Intrinsic::smax:
- case Intrinsic::usub_sat:
- case Intrinsic::uadd_sat:
- case Intrinsic::ssub_sat:
- case Intrinsic::sadd_sat:
+ if (isLanewiseOperation(II))
for (Value *Op : II->args())
if (auto *Sel = dyn_cast<SelectInst>(Op))
if (Instruction *R = FoldOpIntoSelect(*II, Sel))
return R;
- [[fallthrough]];
- default:
- break;
- }
if (Instruction *Shuf = foldShuffledIntrinsicOperands(II, Builder))
return Shuf;
diff --git a/llvm/test/Transforms/InstCombine/ispow2.ll b/llvm/test/Transforms/InstCombine/ispow2.ll
index c21ad95f83a1c4..2bc629a5cddf24 100644
--- a/llvm/test/Transforms/InstCombine/ispow2.ll
+++ b/llvm/test/Transforms/InstCombine/ispow2.ll
@@ -977,11 +977,7 @@ define i1 @is_pow2or0_ctpop_wrong_pred2_logical(i32 %x) {
define <2 x i1> @is_pow2or0_ctpop_commute_vec_wrong_pred3(<2 x i8> %x) {
; CHECK-LABEL: @is_pow2or0_ctpop_commute_vec_wrong_pred3(
-; CHECK-NEXT: [[T0:%.*]] = tail call range(i8 0, 9) <2 x i8> @llvm.ctpop.v2i8(<2 x i8> [[X:%.*]])
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i8> [[T0]], <i8 1, i8 1>
-; CHECK-NEXT: [[ISZERO:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT: [[R:%.*]] = and <2 x i1> [[CMP]], [[ISZERO]]
-; CHECK-NEXT: ret <2 x i1> [[R]]
+; CHECK-NEXT: ret <2 x i1> zeroinitializer
;
%t0 = tail call <2 x i8> @llvm.ctpop.v2i8(<2 x i8> %x)
%cmp = icmp eq <2 x i8> %t0, <i8 1, i8 1>
@@ -1174,11 +1170,7 @@ define i1 @isnot_pow2nor0_ctpop_wrong_pred2_logical(i32 %x) {
define <2 x i1> @isnot_pow2nor0_wrong_pred3_ctpop_commute_vec(<2 x i8> %x) {
; CHECK-LABEL: @isnot_pow2nor0_wrong_pred3_ctpop_commute_vec(
-; CHECK-NEXT: [[T0:%.*]] = tail call range(i8 0, 9) <2 x i8> @llvm.ctpop.v2i8(<2 x i8> [[X:%.*]])
-; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[T0]], <i8 1, i8 1>
-; CHECK-NEXT: [[NOTZERO:%.*]] = icmp ne <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT: [[R:%.*]] = or <2 x i1> [[CMP]], [[NOTZERO]]
-; CHECK-NEXT: ret <2 x i1> [[R]]
+; CHECK-NEXT: ret <2 x i1> <i1 true, i1 true>
;
%t0 = tail call <2 x i8> @llvm.ctpop.v2i8(<2 x i8> %x)
%cmp = icmp ne <2 x i8> %t0, <i8 1, i8 1>
diff --git a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
index cc8f5d53fddddd..2e6fe26c03051b 100644
--- a/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
+++ b/llvm/test/Transforms/InstCombine/select-ctlz-to-cttz.ll
@@ -208,10 +208,8 @@ define <2 x i32> @select_clz_to_ctz_vec_with_undef(<2 x i32> %a) {
; CHECK-NEXT: [[SUB:%.*]] = sub <2 x i32> zeroinitializer, [[A:%.*]]
; CHECK-NEXT: [[AND:%.*]] = and <2 x i32> [[A]], [[SUB]]
; CHECK-NEXT: [[LZ:%.*]] = tail call range(i32 0, 33) <2 x i32> @llvm.ctlz.v2i32(<2 x i32> [[AND]], i1 true)
-; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq <2 x i32> [[A]], zeroinitializer
; CHECK-NEXT: [[SUB1:%.*]] = xor <2 x i32> [[LZ]], <i32 31, i32 undef>
-; CHECK-NEXT: [[COND:%.*]] = select <2 x i1> [[TOBOOL]], <2 x i32> [[LZ]], <2 x i32> [[SUB1]]
-; CHECK-NEXT: ret <2 x i32> [[COND]]
+; CHECK-NEXT: ret <2 x i32> [[SUB1]]
;
%sub = sub <2 x i32> zeroinitializer, %a
%and = and <2 x i32> %sub, %a
diff --git a/llvm/test/Transforms/InstSimplify/select.ll b/llvm/test/Transforms/InstSimplify/select.ll
index 1e503afae1a69d..4618e1143dc8f9 100644
--- a/llvm/test/Transforms/InstSimplify/select.ll
+++ b/llvm/test/Transforms/InstSimplify/select.ll
@@ -1087,13 +1087,10 @@ define i32 @select_ctpop_zero(i32 %x) {
ret i32 %sel
}
-; FIXME: This is safe to fold.
define <2 x i32> @select_ctpop_zero_vec(<2 x i32> %x) {
; CHECK-LABEL: @select_ctpop_zero_vec(
-; CHECK-NEXT: [[T0:%.*]] = icmp eq <2 x i32> [[X:%.*]], zeroinitializer
-; CHECK-NEXT: [[T1:%.*]] = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> [[X]])
-; CHECK-NEXT: [[SEL:%.*]] = select <2 x i1> [[T0]], <2 x i32> zeroinitializer, <2 x i32> [[T1]]
-; CHECK-NEXT: ret <2 x i32> [[SEL]]
+; CHECK-NEXT: [[T1:%.*]] = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> [[X:%.*]])
+; CHECK-NEXT: ret <2 x i32> [[T1]]
;
%t0 = icmp eq <2 x i32> %x, zeroinitializer
%t1 = call <2 x i32> @llvm.ctpop.v2i32(<2 x i32> %x)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Naming wise, isNotLaneCrossingOperation seems like a double negative. I believe we've called this lane or element wise operations elsewhere, maybe isLanewiseOperation would be better? |
See this comment thread: #112011 (comment) |
Distinction understood, but seems like either a) we should add the ElementWise predicate too because many operations are element wise or b) we should at least update the doc comment to explain the distinction. I certainly hadn't gotten it from either skimming this review or the change. |
See #112375. |
Factor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isNotCrossLaneOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/68H4ka
Factor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isNotCrossLaneOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/68H4ka
Factor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isNotCrossLaneOperation in ValueTracking. Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/68H4ka
Factor out and unify common code from InstSimplify and InstCombine that partially guard against cross-lane vector operations into llvm::isNotCrossLaneOperation in ValueTracking.
Alive2 proofs for changed tests: https://alive2.llvm.org/ce/z/68H4ka