From 2f1c4b5107aec730b35fd8eae90f7be33a6e5cc2 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 12 Jun 2025 06:01:16 +0000 Subject: [PATCH 1/7] [msan] Fix shadow computation for partially undefined constant fixed-length vectors For each element of the vector, the shadow is initialized iff the element is not undefined/poisoned. Previously, partially undefined constant fixed-length vectors always had fully initialized shadows, which leads to false negatives. Updates the tests from https://github.com/llvm/llvm-project/pull/143823 Note: since this patch corrects a false negative, MSan can now detect more bugs (corollary: code/tests that passed MSan may start failing). --- .../Instrumentation/MemorySanitizer.cpp | 23 +++++++++++++++++-- .../MemorySanitizer/partial-poison.ll | 11 ++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index d3c6a7151ec37..b345d9dc06654 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -1989,6 +1989,8 @@ struct MemorySanitizerVisitor : public InstVisitor { } return Shadow; } + // Handle fully undefined values + // (partially undefined constant vectors are handled later) if (UndefValue *U = dyn_cast(V)) { Value *AllOnes = (PropagateShadow && PoisonUndef) ? getPoisonedShadow(V) : getCleanShadow(V); @@ -2086,8 +2088,25 @@ struct MemorySanitizerVisitor : public InstVisitor { return ShadowPtr; } - // TODO: Partially undefined vectors are handled by the fall-through case - // below (see partial-poison.ll); this causes false negatives. + // Check for partially undefined constant vectors + // TODO: scalable vectors (this is hard because we do not have IRBuilder) + if ( isa(V->getType()) + && isa(V) + && (cast(V))->containsUndefOrPoisonElement() + && PropagateShadow + && PoisonUndef) { + unsigned NumElems = (cast(V->getType()))->getNumElements(); + SmallVector ShadowVector(NumElems); + for (unsigned i = 0; i != NumElems; ++i) { + Constant *Elem = (cast(V))->getAggregateElement(i); + ShadowVector[i] = isa(Elem) ? getPoisonedShadow(Elem) : getCleanShadow(Elem); + } + + Value *ShadowConstant = ConstantVector::get(ShadowVector); + LLVM_DEBUG(dbgs() << "Partial undef constant vector: " << *V << " ==> " << *ShadowConstant << "\n"); + + return ShadowConstant; + } // For everything else the shadow is zero. return getCleanShadow(V); diff --git a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll index 5164441c17e10..e058086b040ad 100644 --- a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll @@ -1,8 +1,7 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -S -passes='msan' 2>&1 | FileCheck %s ; -; Test case to show that MSan computes shadows for partially poisoned vectors -; as fully initialized, resulting in false negatives. +; Regression test case for computing shadows of partially poisoned vectors target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -11,7 +10,7 @@ define <2 x i64> @left_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -21,7 +20,7 @@ define <2 x i64> @right_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -51,7 +50,7 @@ define <2 x i64> @left_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -61,7 +60,7 @@ define <2 x i64> @right_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> From 2f3fa0b6815e8c1f86d4d12ad223206210b25aba Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 12 Jun 2025 06:11:24 +0000 Subject: [PATCH 2/7] clang-format --- .../Instrumentation/MemorySanitizer.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index b345d9dc06654..9464a3918d4af 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -2090,20 +2090,21 @@ struct MemorySanitizerVisitor : public InstVisitor { // Check for partially undefined constant vectors // TODO: scalable vectors (this is hard because we do not have IRBuilder) - if ( isa(V->getType()) - && isa(V) - && (cast(V))->containsUndefOrPoisonElement() - && PropagateShadow - && PoisonUndef) { - unsigned NumElems = (cast(V->getType()))->getNumElements(); + if (isa(V->getType()) && isa(V) && + (cast(V))->containsUndefOrPoisonElement() && + PropagateShadow && PoisonUndef) { + unsigned NumElems = + (cast(V->getType()))->getNumElements(); SmallVector ShadowVector(NumElems); for (unsigned i = 0; i != NumElems; ++i) { Constant *Elem = (cast(V))->getAggregateElement(i); - ShadowVector[i] = isa(Elem) ? getPoisonedShadow(Elem) : getCleanShadow(Elem); + ShadowVector[i] = isa(Elem) ? getPoisonedShadow(Elem) + : getCleanShadow(Elem); } Value *ShadowConstant = ConstantVector::get(ShadowVector); - LLVM_DEBUG(dbgs() << "Partial undef constant vector: " << *V << " ==> " << *ShadowConstant << "\n"); + LLVM_DEBUG(dbgs() << "Partial undef constant vector: " << *V << " ==> " + << *ShadowConstant << "\n"); return ShadowConstant; } From 97bfb3444939d7f166fc4b48e7e3fd652ac69496 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 12 Jun 2025 21:00:02 +0000 Subject: [PATCH 3/7] Add 'msan-poison-undef-vectors' for shitty compilers Also update tests and TODOs --- .../Instrumentation/MemorySanitizer.cpp | 16 +++++++- .../MemorySanitizer/partial-poison.ll | 40 +++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index 9464a3918d4af..7584bf9240e3c 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -269,6 +269,14 @@ static cl::opt ClPoisonUndef("msan-poison-undef", cl::desc("poison undef temps"), cl::Hidden, cl::init(true)); +static cl::opt ClPoisonUndefVectors( + "msan-poison-undef-vectors", + cl::desc("Precisely poison partially undefined constant vectors. " + "If false (legacy behavior), the entire vector is " + "considered fully initialized, which may lead to false " + "negatives."), + cl::Hidden, cl::init(true)); + static cl::opt ClHandleICmp("msan-handle-icmp", cl::desc("propagate shadow through ICmpEQ and ICmpNE"), @@ -1181,6 +1189,7 @@ struct MemorySanitizerVisitor : public InstVisitor { bool PropagateShadow; bool PoisonStack; bool PoisonUndef; + bool PoisonUndefVectors; struct ShadowOriginAndInsertPoint { Value *Shadow; @@ -1207,6 +1216,7 @@ struct MemorySanitizerVisitor : public InstVisitor { PropagateShadow = SanitizeFunction; PoisonStack = SanitizeFunction && ClPoisonStack; PoisonUndef = SanitizeFunction && ClPoisonUndef; + PoisonUndefVectors = SanitizeFunction && ClPoisonUndefVectors; // In the presence of unreachable blocks, we may see Phi nodes with // incoming nodes from such blocks. Since InstVisitor skips unreachable @@ -2088,11 +2098,11 @@ struct MemorySanitizerVisitor : public InstVisitor { return ShadowPtr; } - // Check for partially undefined constant vectors + // Check for partially-undefined constant vectors // TODO: scalable vectors (this is hard because we do not have IRBuilder) if (isa(V->getType()) && isa(V) && (cast(V))->containsUndefOrPoisonElement() && - PropagateShadow && PoisonUndef) { + PropagateShadow && PoisonUndefVectors) { unsigned NumElems = (cast(V->getType()))->getNumElements(); SmallVector ShadowVector(NumElems); @@ -2109,6 +2119,8 @@ struct MemorySanitizerVisitor : public InstVisitor { return ShadowConstant; } + // TODO: partially-undefined constant arrays, structures, and nested types + // For everything else the shadow is zero. return getCleanShadow(V); } diff --git a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll index e058086b040ad..184794f124e5a 100644 --- a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll @@ -1,7 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; RUN: opt < %s -S -passes='msan' 2>&1 | FileCheck %s +; RUN: opt < %s -S -passes='msan' 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-PRECISE +; RUN: opt < %s -S -passes='msan' -msan-poison-undef-vectors=false 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-IMPRECISE ; -; Regression test case for computing shadows of partially poisoned vectors +; Regression test case for computing shadows of partially poisoned vectors. +; Partially poisoned structs and arrays are not correctly implemented. target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -10,7 +12,8 @@ define <2 x i64> @left_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -20,7 +23,8 @@ define <2 x i64> @right_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -50,7 +54,8 @@ define <2 x i64> @left_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -60,7 +65,8 @@ define <2 x i64> @right_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -70,8 +76,28 @@ define <2 x i64> @full_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @full_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> splat (i64 -1), ptr @__msan_retval_tls, align 8 +; CHECK: store <2 x i64> splat (i64 -1), ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> undef ; ret <2 x i64> } + +define {i64, i64} @struct_left_undef() sanitize_memory { +; CHECK-LABEL: define { i64, i64 } @struct_left_undef( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: call void @llvm.donothing() +; CHECK-NEXT: store { i64, i64 } zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: ret { i64, i64 } { i64 undef, i64 42 } +; + ret {i64, i64} { i64 undef, i64 42 } +} + +define [2x i64] @array_right_undef() sanitize_memory { +; CHECK-LABEL: define [2 x i64] @array_right_undef( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: call void @llvm.donothing() +; CHECK-NEXT: store [2 x i64] zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: ret [2 x i64] [i64 42, i64 undef] +; + ret [2x i64] [ i64 42, i64 undef ] +} From 09e88e65449cafd1c8148c62976908e8adddc087 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 12 Jun 2025 21:01:46 +0000 Subject: [PATCH 4/7] Save money on parenthesis licensing fees --- llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index 7584bf9240e3c..020267c4582da 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -2101,13 +2101,12 @@ struct MemorySanitizerVisitor : public InstVisitor { // Check for partially-undefined constant vectors // TODO: scalable vectors (this is hard because we do not have IRBuilder) if (isa(V->getType()) && isa(V) && - (cast(V))->containsUndefOrPoisonElement() && - PropagateShadow && PoisonUndefVectors) { - unsigned NumElems = - (cast(V->getType()))->getNumElements(); + cast(V)->containsUndefOrPoisonElement() && PropagateShadow && + PoisonUndefVectors) { + unsigned NumElems = cast(V->getType())->getNumElements(); SmallVector ShadowVector(NumElems); for (unsigned i = 0; i != NumElems; ++i) { - Constant *Elem = (cast(V))->getAggregateElement(i); + Constant *Elem = cast(V)->getAggregateElement(i); ShadowVector[i] = isa(Elem) ? getPoisonedShadow(Elem) : getCleanShadow(Elem); } From 8db04fb22a9c98fdb2aa9f974e2956f626f9ff6d Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 12 Jun 2025 23:44:06 +0000 Subject: [PATCH 5/7] Clarify non-interaction of msan-poison-undef and msan-poison-undef-vectors. --- .../Transforms/Instrumentation/MemorySanitizer.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index 020267c4582da..e3afa93b8a670 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -265,16 +265,21 @@ static cl::opt cl::desc("Print name of local stack variable"), cl::Hidden, cl::init(true)); -static cl::opt ClPoisonUndef("msan-poison-undef", - cl::desc("poison undef temps"), cl::Hidden, - cl::init(true)); +static cl::opt + ClPoisonUndef("msan-poison-undef", + cl::desc("Poison fully undef temporary values. " + "Partially undefined constant vectors " + "are unaffected by this flag (see " + "-msan-poison-undef-vectors)."), + cl::Hidden, cl::init(true)); static cl::opt ClPoisonUndefVectors( "msan-poison-undef-vectors", cl::desc("Precisely poison partially undefined constant vectors. " "If false (legacy behavior), the entire vector is " "considered fully initialized, which may lead to false " - "negatives."), + "negatives. Fully undefined constant vectors are " + "unaffected by this flag (see -msan-poison-undef)."), cl::Hidden, cl::init(true)); static cl::opt From 4d4a8055ab51566f4fc31269a4c8863aa8191e6e Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Fri, 13 Jun 2025 05:27:26 +0000 Subject: [PATCH 6/7] Add back CHECK-NEXT --- llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll index 184794f124e5a..f0a6035608a25 100644 --- a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll @@ -76,7 +76,7 @@ define <2 x i64> @full_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @full_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK: store <2 x i64> splat (i64 -1), ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: store <2 x i64> splat (i64 -1), ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> undef ; ret <2 x i64> From 82c463c65d66ee81be1e73846b325ed6023f39b2 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Fri, 13 Jun 2025 22:44:23 +0000 Subject: [PATCH 7/7] Turn fix off by default --- llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp | 2 +- llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index e3afa93b8a670..3941bed37ebaf 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -280,7 +280,7 @@ static cl::opt ClPoisonUndefVectors( "considered fully initialized, which may lead to false " "negatives. Fully undefined constant vectors are " "unaffected by this flag (see -msan-poison-undef)."), - cl::Hidden, cl::init(true)); + cl::Hidden, cl::init(false)); static cl::opt ClHandleICmp("msan-handle-icmp", diff --git a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll index f0a6035608a25..025317a53c8d1 100644 --- a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; RUN: opt < %s -S -passes='msan' 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-PRECISE +; RUN: opt < %s -S -passes='msan' -msan-poison-undef-vectors=true 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-PRECISE ; RUN: opt < %s -S -passes='msan' -msan-poison-undef-vectors=false 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-IMPRECISE ; ; Regression test case for computing shadows of partially poisoned vectors.