diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -5311,31 +5311,56 @@ VectorType *SubVecTy = cast(SubVec->getType()); VectorType *ResultTy = cast(Call.getType()); + ElementCount VecEC = VecTy->getElementCount(); + ElementCount SubVecEC = SubVecTy->getElementCount(); + Assert(VecTy == ResultTy, "experimental_vector_insert vector type must match result type."); Assert(VecTy->getElementType() == SubVecTy->getElementType(), "experimental_vector_insert parameters must have the same element " "type.", &Call); - Assert(IdxN % SubVecTy->getElementCount().getKnownMinValue() == 0, + Assert(IdxN % SubVecEC.getKnownMinValue() == 0, "experimental_vector_insert index must be a constant multiple of " "the subvector's known minimum vector length."); + + // If this insertion is not the 'mixed' case where a fixed vector is + // inserted into a scalable vector, ensure that the insertion of the + // subvector does not overrun the parent vector. + if (VecEC.isScalable() == SubVecEC.isScalable()) { + Assert( + IdxN + SubVecEC.getKnownMinValue() <= VecEC.getKnownMinValue(), + "subvector operand of experimental_vector_insert would overrun the " + "vector being inserted into."); + } break; } case Intrinsic::experimental_vector_extract: { Value *Vec = Call.getArgOperand(0); Value *Idx = Call.getArgOperand(1); unsigned IdxN = cast(Idx)->getZExtValue(); + VectorType *ResultTy = cast(Call.getType()); VectorType *VecTy = cast(Vec->getType()); + ElementCount VecEC = VecTy->getElementCount(); + ElementCount ResultEC = ResultTy->getElementCount(); + Assert(ResultTy->getElementType() == VecTy->getElementType(), "experimental_vector_extract result must have the same element " "type as the input vector.", &Call); - Assert(IdxN % ResultTy->getElementCount().getKnownMinValue() == 0, + Assert(IdxN % ResultEC.getKnownMinValue() == 0, "experimental_vector_extract index must be a constant multiple of " "the result type's known minimum vector length."); + + // If this extraction is not the 'mixed' case where a fixed vector is is + // extracted from a scalable vector, ensure that the extraction does not + // overrun the parent vector. + if (VecEC.isScalable() == ResultEC.isScalable()) { + Assert(IdxN + ResultEC.getKnownMinValue() <= VecEC.getKnownMinValue(), + "experimental_vector_extract would overrun."); + } break; } case Intrinsic::experimental_noalias_scope_decl: { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1863,12 +1863,6 @@ unsigned SubVecNumElts = SubVecTy->getNumElements(); unsigned IdxN = cast(Idx)->getZExtValue(); - // The result of this call is undefined if the insertion overruns Vec. - if (IdxN + SubVecNumElts > VecNumElts) { - replaceInstUsesWith(CI, UndefValue::get(CI.getType())); - return eraseInstFromFunction(CI); - } - // An insert that entirely overwrites Vec with SubVec is a nop. if (VecNumElts == SubVecNumElts) { replaceInstUsesWith(CI, SubVec); @@ -1916,12 +1910,6 @@ unsigned VecNumElts = VecTy->getNumElements(); unsigned IdxN = cast(Idx)->getZExtValue(); - // The result of this call is undefined if the extraction overruns Vec. - if (IdxN + DstNumElts > VecNumElts) { - replaceInstUsesWith(CI, UndefValue::get(CI.getType())); - return eraseInstFromFunction(CI); - } - // Extracting the entirety of Vec is a nop. if (VecNumElts == DstNumElts) { replaceInstUsesWith(CI, Vec); diff --git a/llvm/test/Verifier/insert-extract-intrinsics-invalid.ll b/llvm/test/Verifier/insert-extract-intrinsics-invalid.ll --- a/llvm/test/Verifier/insert-extract-intrinsics-invalid.ll +++ b/llvm/test/Verifier/insert-extract-intrinsics-invalid.ll @@ -1,5 +1,9 @@ ; RUN: not opt -verify -S < %s 2>&1 >/dev/null | FileCheck %s +; +; Test that extractions/insertion indices are validated. +; + ; CHECK: experimental_vector_extract index must be a constant multiple of the result type's known minimum vector length. define <4 x i32> @extract_idx_not_constant_multiple(<8 x i32> %vec) { %1 = call <4 x i32> @llvm.experimental.vector.extract.v4i32.v8i32(<8 x i32> %vec, i64 1) @@ -12,5 +16,57 @@ ret <8 x i32> %1 } +; +; Test that extractions/insertions which 'overrun' are captured. +; + +; CHECK: experimental_vector_extract would overrun. +define <3 x i32> @extract_overrun_fixed_fixed(<8 x i32> %vec) { + %1 = call <3 x i32> @llvm.experimental.vector.extract.v8i32.v3i32(<8 x i32> %vec, i64 6) + ret <3 x i32> %1 +} + +; CHECK: experimental_vector_extract would overrun. +define @extract_overrun_scalable_scalable( %vec) { + %1 = call @llvm.experimental.vector.extract.nxv8i32.nxv3i32( %vec, i64 6) + ret %1 +} + +; We cannot statically check whether or not an extraction of a fixed vector +; from a scalable vector would overrun, because we can't compare the sizes of +; the two. Therefore, this function should not raise verifier errors. +; CHECK-NOT: experimental_vector_extract would overrun. +define <3 x i32> @extract_overrun_scalable_fixed( %vec) { + %1 = call <3 x i32> @llvm.experimental.vector.extract.nxv8i32.v3i32( %vec, i64 6) + ret <3 x i32> %1 +} + +; CHECK: subvector operand of experimental_vector_insert would overrun the vector being inserted into. +define <8 x i32> @insert_overrun_fixed_fixed(<8 x i32> %vec, <3 x i32> %subvec) { + %1 = call <8 x i32> @llvm.experimental.vector.insert.v8i32.v3i32(<8 x i32> %vec, <3 x i32> %subvec, i64 6) + ret <8 x i32> %1 +} + +; CHECK: subvector operand of experimental_vector_insert would overrun the vector being inserted into. +define @insert_overrun_scalable_scalable( %vec, %subvec) { + %1 = call @llvm.experimental.vector.insert.nxv8i32.nxv3i32( %vec, %subvec, i64 6) + ret %1 +} + +; We cannot statically check whether or not an insertion of a fixed vector into +; a scalable vector would overrun, because we can't compare the sizes of the +; two. Therefore, this function should not raise verifier errors. +; CHECK-NOT: subvector operand of experimental_vector_insert would overrun the vector being inserted into. +define @insert_overrun_scalable_fixed( %vec, <3 x i32> %subvec) { + %1 = call @llvm.experimental.vector.insert.nxv8i32.v3i32( %vec, <3 x i32> %subvec, i64 6) + ret %1 +} + +declare @llvm.experimental.vector.extract.nxv8i32.nxv3i32(, i64) +declare @llvm.experimental.vector.insert.nxv8i32.nxv3i32(, , i64) +declare @llvm.experimental.vector.insert.nxv8i32.v3i32(, <3 x i32>, i64) +declare <3 x i32> @llvm.experimental.vector.extract.nxv8i32.v3i32(, i64) +declare <3 x i32> @llvm.experimental.vector.extract.v8i32.v3i32(<8 x i32>, i64) declare <4 x i32> @llvm.experimental.vector.extract.v4i32.v8i32(<8 x i32>, i64) +declare <8 x i32> @llvm.experimental.vector.insert.v8i32.v3i32(<8 x i32>, <3 x i32>, i64) declare <8 x i32> @llvm.experimental.vector.insert.v8i32.v4i32(<8 x i32>, <4 x i32>, i64)