[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