[llvm] [llubi] Fix the storage kind of poison results (PR #200830)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 07:56:21 PDT 2026


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/200830

>From 8e19ea9d2bb58e8139509d16bd2c8363ccfe6c0c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 22:09:15 +0800
Subject: [PATCH 1/2] [llubi] Fix the storage kind of poison results

---
 llvm/test/tools/llubi/intr_vector_manip.ll | 24 +++++++++----------
 llvm/tools/llubi/lib/Interpreter.cpp       | 28 ++++++++++++----------
 llvm/tools/llubi/lib/Library.cpp           |  8 +++----
 llvm/tools/llubi/lib/Value.h               | 20 ++++++++++++++++
 4 files changed, 51 insertions(+), 29 deletions(-)

diff --git a/llvm/test/tools/llubi/intr_vector_manip.ll b/llvm/test/tools/llubi/intr_vector_manip.ll
index 20895550565d9..9a55ec2dac20a 100644
--- a/llvm/test/tools/llubi/intr_vector_manip.ll
+++ b/llvm/test/tools/llubi/intr_vector_manip.ll
@@ -41,26 +41,26 @@ define void @main() {
 ; CHECK-NEXT:   %insert_mid = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 10, i32 11>, i64 2) => { i32 0, i32 1, i32 10, i32 11, i32 4, i32 5 }
 ; CHECK-NEXT:   %insert_poison_lane = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, <2 x i32> <i32 poison, i32 11>, i64 2) => { i32 0, i32 1, poison, i32 11, i32 4, i32 5 }
 ; CHECK-NEXT:   %insert_tail = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 4) => { i32 0, i32 0, i32 0, i32 0, i32 9, i32 10 }
-; CHECK-NEXT:   %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 6) => poison
+; CHECK-NEXT:   %insert_poison = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 9, i32 10>, i64 6) => { poison, poison, poison, poison, poison, poison }
 ; CHECK-NEXT:   %extract_mid = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 2) => { i32 2, i32 3 }
 ; CHECK-NEXT:   %extract_poison_lane = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 poison, i32 2, i32 3, i32 4, i32 5>, i64 0) => { i32 0, poison }
 ; CHECK-NEXT:   %extract_tail = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 4) => { i32 4, i32 5 }
-; CHECK-NEXT:   %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 6) => poison
+; CHECK-NEXT:   %extract_poison = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>, i64 6) => { poison, poison }
 ; CHECK-NEXT:   %reverse = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>) => { i32 3, i32 2, i32 1, i32 0 }
 ; CHECK-NEXT:   %reverse_poison = call <4 x i32> @llvm.vector.reverse.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>) => { i32 3, i32 2, poison, i32 0 }
 ; CHECK-NEXT:   %splice_left = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 2) => { i32 2, i32 3, i32 10, i32 11 }
 ; CHECK-NEXT:   %splice_left_poison_lane = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1) => { poison, i32 2, i32 3, i32 10 }
-; CHECK-NEXT:   %splice_left_poison = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => poison
+; CHECK-NEXT:   %splice_left_poison = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> <i32 0, i32 poison, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => { poison, poison, poison, poison }
 ; CHECK-NEXT:   %splice_right = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 1) => { i32 3, i32 10, i32 11, i32 12 }
 ; CHECK-NEXT:   %splice_right_full_rhs = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 0) => { i32 10, i32 11, i32 12, i32 13 }
-; CHECK-NEXT:   %splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => poison
-; CHECK-NEXT:   %extract_poison_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison) => poison
-; CHECK-NEXT:   %insert_poison_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison) => poison
-; CHECK-NEXT:   %splice_left_poison_idx = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => poison
-; CHECK-NEXT:   %splice_right_poison_idx = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => poison
-; CHECK-NEXT:   %insert_bad_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> zeroinitializer, i64 1) => poison
-; CHECK-NEXT:   %extract_bad_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 1) => poison
-; CHECK-NEXT:   %insert_idx_overflow = call <vscale x 4 x i32> @llvm.vector.insert.nxv4i32.nxv2i32(<vscale x 4 x i32> zeroinitializer, <vscale x 2 x i32> zeroinitializer, i64 -9223372036854775808) => poison
-; CHECK-NEXT:   %extract_idx_overflow = call <vscale x 2 x i32> @llvm.vector.extract.nxv2i32.nxv4i32(<vscale x 4 x i32> zeroinitializer, i64 -9223372036854775808) => poison
+; CHECK-NEXT:   %splice_right_poison = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> <i32 0, i32 1, i32 2, i32 3>, <4 x i32> <i32 10, i32 11, i32 12, i32 13>, i32 5) => { poison, poison, poison, poison }
+; CHECK-NEXT:   %extract_poison_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 poison) => { poison, poison }
+; CHECK-NEXT:   %insert_poison_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> <i32 1, i32 2>, i64 poison) => { poison, poison, poison, poison, poison, poison }
+; CHECK-NEXT:   %splice_left_poison_idx = call <4 x i32> @llvm.vector.splice.left.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => { poison, poison, poison, poison }
+; CHECK-NEXT:   %splice_right_poison_idx = call <4 x i32> @llvm.vector.splice.right.v4i32(<4 x i32> zeroinitializer, <4 x i32> zeroinitializer, i32 poison) => { poison, poison, poison, poison }
+; CHECK-NEXT:   %insert_bad_idx = call <6 x i32> @llvm.vector.insert.v6i32.v2i32(<6 x i32> zeroinitializer, <2 x i32> zeroinitializer, i64 1) => { poison, poison, poison, poison, poison, poison }
+; CHECK-NEXT:   %extract_bad_idx = call <2 x i32> @llvm.vector.extract.v2i32.v6i32(<6 x i32> zeroinitializer, i64 1) => { poison, poison }
+; CHECK-NEXT:   %insert_idx_overflow = call <vscale x 4 x i32> @llvm.vector.insert.nxv4i32.nxv2i32(<vscale x 4 x i32> zeroinitializer, <vscale x 2 x i32> zeroinitializer, i64 -9223372036854775808) => { poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison, poison }
+; CHECK-NEXT:   %extract_idx_overflow = call <vscale x 2 x i32> @llvm.vector.extract.nxv2i32.nxv4i32(<vscale x 4 x i32> zeroinitializer, i64 -9223372036854775808) => { poison, poison, poison, poison, poison, poison, poison, poison }
 ; CHECK-NEXT:   ret void
 ; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 3c21f8ff175f7..c98622e4bb83e 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -161,7 +161,9 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   void setResult(Instruction &I, AnyValue V) {
     if (!hasProgramExited() && !Handler.onInstructionExecuted(I, V))
       setFailed();
-    CurrentFrame->ValueMap.insert_or_assign(&I, std::move(V));
+    assert(V.isCompatibleWith(I.getType()) && "Unexpected value storage kind.");
+    if (!V.isNone())
+      CurrentFrame->ValueMap.insert_or_assign(&I, std::move(V));
   }
 
   APFloat handleDenormal(APFloat Val, DenormalMode::DenormalModeKind Mode,
@@ -1254,7 +1256,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
     case Intrinsic::vector_insert: {
       if (Args[2].isPoison())
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const auto &Vec = Args[0].asAggregate();
       const auto &SubVec = Args[1].asAggregate();
       const auto &Idx = Args[2].asInteger();
@@ -1263,14 +1265,14 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
       const uint64_t RawOffset = Idx.getZExtValue();
       const uint32_t MinSize = EC.getKnownMinValue();
       if (RawOffset % MinSize != 0)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const uint64_t Chunk = RawOffset / MinSize;
       const uint64_t EVL = Ctx.getEVL(EC);
       if (Chunk > std::numeric_limits<uint64_t>::max() / EVL)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const uint64_t Offset = Chunk * EVL;
       if (Offset > Vec.size() || SubVec.size() > Vec.size() - Offset)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       std::vector<AnyValue> Res;
       Res.reserve(Vec.size());
       for (size_t I = 0; I != Vec.size(); ++I) {
@@ -1283,21 +1285,21 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
     case Intrinsic::vector_extract: {
       if (Args[1].isPoison())
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const auto &Vec = Args[0].asAggregate();
       const auto &Idx = Args[1].asInteger();
       auto EC = cast<VectorType>(RetTy)->getElementCount();
       const uint64_t RawOffset = Idx.getZExtValue();
       const uint32_t MinSize = EC.getKnownMinValue();
       if (RawOffset % MinSize != 0)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const uint64_t Chunk = RawOffset / MinSize;
       const uint64_t EVL = Ctx.getEVL(EC);
       if (Chunk > std::numeric_limits<uint64_t>::max() / EVL)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const uint64_t Offset = Chunk * EVL;
       if (Offset > Vec.size() || EVL > Vec.size() - Offset)
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       return std::vector<AnyValue>(Vec.begin() + Offset,
                                    Vec.begin() + Offset + EVL);
     }
@@ -1350,13 +1352,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
     case Intrinsic::vector_splice_left: {
       if (Args[2].isPoison())
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const auto &LHS = Args[0].asAggregate();
       const auto &RHS = Args[1].asAggregate();
       const auto &Off = Args[2].asInteger();
       const size_t Len = LHS.size();
       if (Off.ugt(Len))
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       uint64_t Offset = Off.getZExtValue();
       std::vector<AnyValue> Res;
       Res.reserve(Len);
@@ -1368,13 +1370,13 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
     }
     case Intrinsic::vector_splice_right: {
       if (Args[2].isPoison())
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       const auto &LHS = Args[0].asAggregate();
       const auto &RHS = Args[1].asAggregate();
       const auto &Off = Args[2].asInteger();
       const size_t Len = LHS.size();
       if (Off.ugt(Len))
-        return AnyValue::poison();
+        return AnyValue::getPoisonValue(Ctx, RetTy);
       uint64_t Offset = Len - Off.getZExtValue();
       std::vector<AnyValue> Res;
       Res.reserve(Len);
diff --git a/llvm/tools/llubi/lib/Library.cpp b/llvm/tools/llubi/lib/Library.cpp
index c68b223d2d65a..3eab215f933d1 100644
--- a/llvm/tools/llubi/lib/Library.cpp
+++ b/llvm/tools/llubi/lib/Library.cpp
@@ -132,7 +132,7 @@ AnyValue Library::executeFree(ArrayRef<AnyValue> Args) {
   if (!Obj) {
     Executor.reportImmediateUB()
         << "freeing a pointer with nullary provenance.";
-    return AnyValue::poison();
+    return AnyValue();
   }
 
   if (const uint64_t Address = Ptr.address().getZExtValue();
@@ -142,20 +142,20 @@ AnyValue Library::executeFree(ArrayRef<AnyValue> Args) {
            "the start of an allocation. Pointer address: 0x"
         << Twine::utohexstr(Address) << ", allocation base: 0x"
         << Twine::utohexstr(Obj->getAddress()) << ".";
-    return AnyValue::poison();
+    return AnyValue();
   }
 
   if (Obj->getState() == MemoryObjectState::Freed) {
     Executor.reportImmediateUB()
         << "double-freeing a memory object allocated at 0x"
         << Twine::utohexstr(Obj->getAddress()) << ".";
-    return AnyValue::poison();
+    return AnyValue();
   }
 
   if (!Obj->isHeapAllocated()) {
     Executor.reportImmediateUB() << "freeing a non-heap allocation at 0x"
                                  << Twine::utohexstr(Obj->getAddress()) << ".";
-    return AnyValue::poison();
+    return AnyValue();
   }
 
   // Currently we don't check for cases where a memory allocated with C
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 60e656342f116..b4ca0dc938ab5 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -207,6 +207,26 @@ class [[nodiscard]] AnyValue {
   bool isPointer() const { return Kind == StorageKind::Pointer; }
   bool isAggregate() const { return Kind == StorageKind::Aggregate; }
 
+  bool isCompatibleWith(Type *Ty) const {
+    switch (Kind) {
+    case StorageKind::None:
+      return Ty->isVoidTy();
+    case StorageKind::Poison:
+      return Ty->isFloatingPointTy() || Ty->isIntegerTy() || Ty->isPointerTy();
+    case StorageKind::Integer:
+      return Ty->isIntegerTy();
+    case StorageKind::Float:
+      return Ty->isFloatingPointTy();
+    case StorageKind::Pointer:
+      return Ty->isPointerTy();
+    // We don't check elements recursively.
+    case StorageKind::Aggregate:
+      return Ty->isAggregateType() || Ty->isVectorTy();
+    default:
+      llvm_unreachable("Unhandled storage kind.");
+    }
+  }
+
   const APInt &asInteger() const {
     assert(Kind == StorageKind::Integer && "Expect an integer value");
     return IntVal;

>From bec6fcadba19b6c19c616b50924ea1a303252066 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 22:55:57 +0800
Subject: [PATCH 2/2] [llubi] Make linter happy

---
 llvm/tools/llubi/lib/Value.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index b4ca0dc938ab5..089245ce8e417 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -222,9 +222,8 @@ class [[nodiscard]] AnyValue {
     // We don't check elements recursively.
     case StorageKind::Aggregate:
       return Ty->isAggregateType() || Ty->isVectorTy();
-    default:
-      llvm_unreachable("Unhandled storage kind.");
     }
+    llvm_unreachable("Unhandled storage kind.");
   }
 
   const APInt &asInteger() const {



More information about the llvm-commits mailing list