[llvm] [llubi] Add support for byte types (PR #200672)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 23 08:47:28 PDT 2026
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/200672
>From d4ab20c436438a7e15d5b6eed271a30e8a958c0c Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 00:53:06 +0800
Subject: [PATCH 01/21] [llubi] Add support for byte types
---
llvm/tools/llubi/lib/Context.cpp | 103 +++++++++++++++++++++++++++++--
llvm/tools/llubi/lib/Context.h | 2 +
llvm/tools/llubi/lib/Value.cpp | 77 +++++++++++++++++++++++
llvm/tools/llubi/lib/Value.h | 58 ++++++++++++++++-
4 files changed, 235 insertions(+), 5 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index cef55b53e0988..cea78b1cf3942 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -126,6 +126,14 @@ MaterializedConstant Context::getConstantValueImpl(Constant *C) {
return MaterializedConstant(CFP->getValue(), /*Cacheable=*/true);
}
+ if (auto *CB = dyn_cast<ConstantByte>(C)) {
+ if (auto *VecTy = dyn_cast<VectorType>(CB->getType()))
+ return std::vector<AnyValue>(
+ getEVL(VecTy->getElementCount()),
+ AnyValue(ByteValue(CB->getValue(), DL.isLittleEndian())));
+ return ByteValue(CB->getValue(), DL.isLittleEndian());
+ }
+
if (auto *CDS = dyn_cast<ConstantDataSequential>(C)) {
std::vector<AnyValue> Elts;
Elts.reserve(CDS->getNumElements());
@@ -439,7 +447,13 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
uint32_t NumBitsToExtract = NewOffsetInBits - OffsetInBits;
uint32_t NumWords = APInt::getNumWords(NumBitsToExtract);
constexpr uint32_t WordBits = APInt::APINT_BITS_PER_WORD;
- SmallVector<APInt::WordType> RawBits(NumWords);
+ SmallVector<APInt::WordType> RawBits;
+ bool IsByteType = Ty->isByteTy();
+ std::vector<Byte> LogicalBytes;
+ if (IsByteType)
+ LogicalBytes.resize(divideCeil(NumBitsToExtract, 8));
+ else
+ RawBits.resize(NumWords);
bool IsTagValid = Ty->isPointerTy();
SmallVector<APInt::WordType> RawTagBits;
if (Ty->isPointerTy())
@@ -460,7 +474,8 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
uint32_t Mask = (1U << NumBitsInByte) - 1;
// If any of the bits in the byte is poison, the whole value is poison.
- if (~LogicalByte.ConcreteMask & ~LogicalByte.Value & Mask) {
+ if (!IsByteType &&
+ (~LogicalByte.ConcreteMask & ~LogicalByte.Value & Mask)) {
if (ContainsUndefinedBits)
*ContainsUndefinedBits = true;
OffsetInBits = NewOffsetInBits;
@@ -472,14 +487,20 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
if (ContainsUndefinedBits)
*ContainsUndefinedBits = true;
- if (getEffectiveUndefValueBehavior() ==
- UndefValueBehavior::NonDeterministic) {
+ if (!IsByteType && getEffectiveUndefValueBehavior() ==
+ UndefValueBehavior::NonDeterministic) {
// We don't use std::uniform_int_distribution here because it produces
// different results across different library implementations. Instead,
// we directly use the low bits from Rng.
RandomBits = static_cast<uint8_t>(Rng());
}
}
+
+ if (IsByteType) {
+ LogicalBytes[BitsStart / 8] = LogicalByte;
+ continue;
+ }
+
uint8_t ActualBits = ((LogicalByte.Value & LogicalByte.ConcreteMask) |
(RandomBits & ~LogicalByte.ConcreteMask)) &
Mask;
@@ -495,6 +516,19 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
}
}
}
+
+ if (IsByteType) {
+ assert(!CheckPaddingBits &&
+ "Non-vector-element cases should be handled by the fast path.");
+ if (NumBits & 7) {
+ uint8_t Mask = static_cast<uint8_t>((~0U) << (NumBits & 7));
+ LogicalBytes.back().zeroBits(Mask);
+ }
+ if (DL.isBigEndian())
+ std::reverse(LogicalBytes.begin(), LogicalBytes.end());
+ return ByteValue(NumBits, std::move(LogicalBytes));
+ }
+
OffsetInBits = NewOffsetInBits;
APInt Bits(NumBitsToExtract, RawBits);
@@ -530,6 +564,25 @@ AnyValue Context::fromBytes(ArrayRef<Byte> Bytes, Type *Ty,
if (Ty->isIntegerTy() || Ty->isFloatingPointTy() || Ty->isPointerTy())
return fromBytes(ConstBytesView(Bytes, DL), Ty, /*OffsetInBits=*/0,
/*CheckPaddingBits=*/true, ContainsUndefinedBits);
+ if (Ty->isByteTy()) {
+ if (ContainsUndefinedBits) {
+ for (const Byte &V : Bytes)
+ if (V.ConcreteMask != 255) {
+ *ContainsUndefinedBits = true;
+ break;
+ }
+ }
+ unsigned BitWidth = Ty->getByteBitWidth();
+ ByteValue Res(BitWidth, Bytes);
+ if (BitWidth & 7) {
+ uint8_t Mask = static_cast<uint8_t>((~0U) << (BitWidth & 7));
+ if (DL.isLittleEndian())
+ Res.mutableBytes().back().zeroBits(Mask);
+ else
+ Res.mutableBytes().front().zeroBits(Mask);
+ }
+ return AnyValue(std::move(Res));
+ }
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
Type *ElemTy = VecTy->getElementType();
@@ -658,6 +711,27 @@ void Context::toBytes(const AnyValue &Val, Type *Ty, uint32_t OffsetInBits,
WriteBits(NeedsPadding ? AddressBits.zext(NewOffsetInBits - OffsetInBits)
: AddressBits,
&Tag);
+ } else if (Ty->isByteTy()) {
+ assert(!PaddingBits &&
+ "Non-vector-element cases should be handled by the fast path.");
+ auto &ByteVal = Val.asByte();
+ ConstBytesView SrcBytes(ByteVal.bytes(), DL);
+ for (uint32_t I = 0, E = static_cast<uint32_t>(SrcBytes.size()); I != E;
+ ++I) {
+ uint32_t NumBitsInByte = std::min(8U, NumBits - I * 8);
+ uint32_t BitsStart = OffsetInBits + I * 8;
+ uint32_t BitsEnd = BitsStart + NumBitsInByte - 1;
+
+ Bytes[BitsStart / 8].writeByte(
+ static_cast<uint8_t>(((1U << NumBitsInByte) - 1) << (BitsStart % 8)),
+ SrcBytes[I].shl(BitsStart % 8));
+ // If it is a cross-byte access, write the remaining bits to the next
+ // byte.
+ if (((BitsStart ^ BitsEnd) & ~7) != 0)
+ Bytes[BitsEnd / 8].writeByte(
+ static_cast<uint8_t>((1U << (BitsEnd % 8 + 1)) - 1),
+ SrcBytes[I].lshr(8 - (BitsStart % 8)));
+ }
} else {
llvm_unreachable("Unsupported scalar type.");
}
@@ -672,6 +746,13 @@ void Context::toBytes(const AnyValue &Val, Type *Ty,
/*PaddingBits=*/true);
return;
}
+ if (Ty->isByteTy()) {
+ auto &ByteVal = Val.asByte();
+ ArrayRef<Byte> SrcBytes = ByteVal.bytes();
+ assert(Bytes.size() == SrcBytes.size() && "Mismatched byte array.");
+ copy(SrcBytes, Bytes.begin());
+ return;
+ }
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
Type *ElemTy = VecTy->getElementType();
@@ -780,6 +861,20 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
llvm_unreachable("Unsupported scalar type for poison value");
return;
}
+ if (Val.isByte()) {
+ for (Byte &V : Val.asMutableByte().mutableBytes()) {
+ if (V.ConcreteMask == 255)
+ continue;
+ uint8_t OldMask = V.ConcreteMask;
+ V.ConcreteMask = 255;
+ V.Value &= OldMask;
+ if (mayUseNonDeterminism())
+ V.Value |= (Rng() & 255) & ~OldMask;
+ V.TagMask &= OldMask;
+ V.TagValue &= OldMask;
+ }
+ return;
+ }
if (Val.isAggregate()) {
auto &SubVals = Val.asAggregate();
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 097f0d1e0ab8b..da70c9f23e474 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -197,6 +197,8 @@ template <typename ArrayRefT> class BytesView {
auto &operator[](uint32_t Index) {
return Bytes[IsLittleEndian ? Index : Bytes.size() - 1 - Index];
}
+
+ size_t size() const { return Bytes.size(); }
};
using ConstBytesView = BytesView<ArrayRef<Byte>>;
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 4ec892f995ab2..120bc21885e37 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -118,6 +118,9 @@ void AnyValue::print(Context &Ctx, raw_ostream &OS) const {
case StorageKind::Pointer:
PtrVal.print(OS);
break;
+ case StorageKind::Byte:
+ ByteVal.print(OS);
+ break;
case StorageKind::Poison:
OS << "poison";
break;
@@ -147,6 +150,9 @@ void AnyValue::destroy() {
case StorageKind::Pointer:
PtrVal.~Pointer();
break;
+ case StorageKind::Byte:
+ ByteVal.~ByteValue();
+ break;
case StorageKind::Poison:
case StorageKind::None:
break;
@@ -167,6 +173,9 @@ AnyValue::AnyValue(const AnyValue &Other) : Kind(Other.Kind) {
case StorageKind::Pointer:
new (&PtrVal) Pointer(Other.PtrVal);
break;
+ case StorageKind::Byte:
+ new (&ByteVal) ByteValue(Other.ByteVal);
+ break;
case StorageKind::Poison:
case StorageKind::None:
break;
@@ -186,6 +195,9 @@ AnyValue::AnyValue(AnyValue &&Other) : Kind(Other.Kind) {
case StorageKind::Pointer:
new (&PtrVal) Pointer(std::move(Other.PtrVal));
break;
+ case StorageKind::Byte:
+ new (&ByteVal) ByteValue(std::move(Other.ByteVal));
+ break;
case StorageKind::Poison:
case StorageKind::None:
break;
@@ -211,6 +223,9 @@ AnyValue &AnyValue::operator=(const AnyValue &Other) {
case StorageKind::Pointer:
new (&PtrVal) Pointer(Other.PtrVal);
break;
+ case StorageKind::Byte:
+ new (&ByteVal) ByteValue(Other.ByteVal);
+ break;
case StorageKind::Poison:
case StorageKind::None:
break;
@@ -236,6 +251,9 @@ AnyValue &AnyValue::operator=(AnyValue &&Other) {
case StorageKind::Pointer:
new (&PtrVal) Pointer(std::move(Other.PtrVal));
break;
+ case StorageKind::Byte:
+ new (&ByteVal) ByteValue(std::move(Other.ByteVal));
+ break;
case StorageKind::Poison:
case StorageKind::None:
break;
@@ -250,6 +268,9 @@ AnyValue &AnyValue::operator=(AnyValue &&Other) {
AnyValue AnyValue::getPoisonValue(Context &Ctx, Type *Ty) {
if (Ty->isFloatingPointTy() || Ty->isIntegerTy() || Ty->isPointerTy())
return AnyValue::poison();
+ if (Ty->isByteTy())
+ return ByteValue::poison(Ty->getByteBitWidth(),
+ Ctx.getDataLayout().isLittleEndian());
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
uint32_t NumElements = Ctx.getEVL(VecTy->getElementCount());
return AnyValue(std::vector<AnyValue>(NumElements, AnyValue::poison()));
@@ -275,6 +296,8 @@ AnyValue AnyValue::getNullValue(Context &Ctx, Type *Ty) {
return AnyValue(APFloat::getZero(Ty->getFltSemantics()));
if (Ty->isPointerTy())
return Pointer::null(Ty->getPointerAddressSpace(), Ctx.getDataLayout());
+ if (Ty->isByteTy())
+ return ByteValue::zero(Ty->getByteBitWidth());
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
uint32_t NumElements = Ctx.getEVL(VecTy->getElementCount());
return AnyValue(std::vector<AnyValue>(
@@ -300,4 +323,58 @@ AnyValue AnyValue::getVectorSplat(const AnyValue &Scalar, size_t NumElements) {
return AnyValue(std::vector<AnyValue>(NumElements, Scalar));
}
+ByteValue::ByteValue(const APInt &V, bool IsLittleEndian) {}
+ByteValue ByteValue::zero(uint32_t BitWidth) {
+ return ByteValue(
+ BitWidth, std::vector<Byte>(divideCeil(BitWidth, 8), Byte::concrete(0)));
+}
+
+ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) {
+ std::vector<Byte> Val(divideCeil(BitWidth, 8), Byte::poison());
+ // Zero high-bits if it is not a byte-sized value.
+ if (BitWidth & 7) {
+ uint8_t Mask = static_cast<uint8_t>((~0U) << (BitWidth & 7));
+ if (IsLittleEndian)
+ Val.back().zeroBits(Mask);
+ else
+ Val.front().zeroBits(Mask);
+ }
+ return ByteValue(BitWidth, std::move(Val));
+}
+
+void ByteValue::print(raw_ostream &OS) const {
+ OS << 'b' << BitWidth << ' ';
+ for (const Byte &V : Val) {
+ // Try to print a byte in short form
+ if (V.ConcreteMask == 255 && V.TagMask == 0) {
+ // Concrete value without provenance.
+ OS << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15);
+ } else if (V.ConcreteMask == 0 && (V.Value == 0 || V.Value == 255)) {
+ // Poison/undef bytes.
+ OS << (V.Value == 0 ? "!!" : "??");
+ } else {
+ for (uint32_t I = 0; I != 8; ++I) {
+ uint32_t Mask = 1U << (8 - I);
+ if (V.ConcreteMask & Mask)
+ OS << (V.Value & Mask ? '1' : '0');
+ else
+ OS << (V.Value & Mask ? '?' : '!');
+ }
+ if (uint32_t TagMask = V.ConcreteMask & V.TagMask) {
+ // Print tags if available.
+ OS << '(';
+ for (uint32_t I = 0; I != 8; ++I) {
+ uint32_t Mask = 1U << (8 - I);
+ if (TagMask & Mask)
+ OS << (V.TagValue & Mask ? '1' : '0');
+ else
+ OS << '!';
+ }
+ OS << ')';
+ }
+ }
+ OS << ' ';
+ }
+}
+
} // namespace llvm::ubi
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index d02607cdc52f3..96d01cf3d1909 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -79,6 +79,13 @@ struct Byte {
TagValue = (TagValue & ~Mask) | (Tag & Mask);
}
+ void writeByte(uint8_t Mask, const Byte &RHS) {
+ ConcreteMask = (ConcreteMask & ~Mask) | (RHS.ConcreteMask & Mask);
+ Value = (Value & ~Mask) | (RHS.Value & Mask);
+ TagMask = (TagMask & ~Mask) | (RHS.TagMask & Mask);
+ TagValue = (TagValue & ~Mask) | (RHS.TagValue & Mask);
+ }
+
/// Returns a logical byte that is part of two adjacent bytes.
/// Example with ShAmt = 5:
/// | Low | High |
@@ -99,13 +106,20 @@ struct Byte {
static_cast<uint8_t>(TagMask >> Shift),
static_cast<uint8_t>(TagValue >> Shift)};
}
+
+ Byte shl(uint8_t Shift) const {
+ return Byte{static_cast<uint8_t>(ConcreteMask << Shift),
+ static_cast<uint8_t>(Value << Shift),
+ static_cast<uint8_t>(TagMask << Shift),
+ static_cast<uint8_t>(TagValue << Shift)};
+ }
};
-// TODO: Byte
enum class StorageKind {
Integer,
Float,
Pointer,
+ Byte,
Poison,
None, // Placeholder for void type
Aggregate, // Struct, Array or Vector
@@ -220,6 +234,35 @@ class Pointer {
Provenance &provenance() const { return *Prov; }
};
+/// Represents a scalar byte value. If the value is not byte-sized, the high
+/// bits are zero-padded.
+class ByteValue {
+ std::vector<Byte> Val;
+ uint32_t BitWidth;
+
+public:
+ ByteValue(const APInt &V, bool IsLittleEndian);
+ /// The caller is responsible to zero high bits for non-byte-sized values.
+ ByteValue(uint32_t BitWidth, ArrayRef<Byte> Val)
+ : Val(Val), BitWidth(BitWidth) {}
+ /// The caller is responsible to zero high bits for non-byte-sized values.
+ ByteValue(uint32_t BitWidth, std::vector<Byte> Val)
+ : Val(std::move(Val)), BitWidth(BitWidth) {}
+ ByteValue(const ByteValue &) = default;
+ ByteValue(ByteValue &&) = default;
+ ByteValue &operator=(const ByteValue &) = default;
+ ByteValue &operator=(ByteValue &&) = default;
+ ~ByteValue() = default;
+
+ static ByteValue zero(uint32_t BitWidth);
+ static ByteValue poison(uint32_t BitWidth, bool IsLittleEndian);
+
+ uint32_t getBitWidth() const { return BitWidth; }
+ ArrayRef<Byte> bytes() const { return Val; }
+ MutableArrayRef<Byte> mutableBytes() { return Val; }
+ void print(raw_ostream &OS) const;
+};
+
// Value representation for actual values of LLVM values.
// We don't model undef values here (except for byte types).
class [[nodiscard]] AnyValue {
@@ -228,6 +271,7 @@ class [[nodiscard]] AnyValue {
APInt IntVal;
APFloat FloatVal;
Pointer PtrVal;
+ ByteValue ByteVal;
std::vector<AnyValue> AggVal;
};
@@ -240,6 +284,7 @@ class [[nodiscard]] AnyValue {
AnyValue(APInt Val) : Kind(StorageKind::Integer), IntVal(std::move(Val)) {}
AnyValue(APFloat Val) : Kind(StorageKind::Float), FloatVal(std::move(Val)) {}
AnyValue(Pointer Val) : Kind(StorageKind::Pointer), PtrVal(std::move(Val)) {}
+ AnyValue(ByteValue Val) : Kind(StorageKind::Byte), ByteVal(std::move(Val)) {}
AnyValue(std::vector<AnyValue> Val)
: Kind(StorageKind::Aggregate), AggVal(std::move(Val)) {}
AnyValue(const AnyValue &Other);
@@ -261,6 +306,7 @@ class [[nodiscard]] AnyValue {
bool isInteger() const { return Kind == StorageKind::Integer; }
bool isFloat() const { return Kind == StorageKind::Float; }
bool isPointer() const { return Kind == StorageKind::Pointer; }
+ bool isByte() const { return Kind == StorageKind::Byte; }
bool isAggregate() const { return Kind == StorageKind::Aggregate; }
bool isCompatibleWith(Type *Ty) const {
@@ -297,6 +343,16 @@ class [[nodiscard]] AnyValue {
return PtrVal;
}
+ const ByteValue &asByte() const {
+ assert(Kind == StorageKind::Byte && "Expect a byte value");
+ return ByteVal;
+ }
+
+ ByteValue &asMutableByte() {
+ assert(Kind == StorageKind::Byte && "Expect a byte value");
+ return ByteVal;
+ }
+
const std::vector<AnyValue> &asAggregate() const {
assert(Kind == StorageKind::Aggregate &&
"Expect an aggregate/vector value");
>From eff86081aada92a9c14ffe6f4000226cc190664d Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 08:46:15 +0800
Subject: [PATCH 02/21] [llubi] Minor fixes
---
llvm/tools/llubi/lib/Context.cpp | 14 ++++-------
llvm/tools/llubi/lib/Context.h | 4 +++-
llvm/tools/llubi/lib/Value.cpp | 35 +++++++++++++++++++---------
llvm/tools/llubi/lib/Value.h | 40 +++++++++++++++++++++++++-------
4 files changed, 63 insertions(+), 30 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index cea78b1cf3942..b18faa017369d 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -497,7 +497,7 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
}
if (IsByteType) {
- LogicalBytes[BitsStart / 8] = LogicalByte;
+ LogicalBytes[I / 8] = LogicalByte;
continue;
}
@@ -526,7 +526,7 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
}
if (DL.isBigEndian())
std::reverse(LogicalBytes.begin(), LogicalBytes.end());
- return ByteValue(NumBits, std::move(LogicalBytes));
+ return ByteValue(NumBits, std::move(LogicalBytes), DL.isLittleEndian());
}
OffsetInBits = NewOffsetInBits;
@@ -573,14 +573,8 @@ AnyValue Context::fromBytes(ArrayRef<Byte> Bytes, Type *Ty,
}
}
unsigned BitWidth = Ty->getByteBitWidth();
- ByteValue Res(BitWidth, Bytes);
- if (BitWidth & 7) {
- uint8_t Mask = static_cast<uint8_t>((~0U) << (BitWidth & 7));
- if (DL.isLittleEndian())
- Res.mutableBytes().back().zeroBits(Mask);
- else
- Res.mutableBytes().front().zeroBits(Mask);
- }
+ ByteValue Res(BitWidth, Bytes, DL.isLittleEndian(),
+ /*ImplicitClearHighBits=*/true);
return AnyValue(std::move(Res));
}
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index da70c9f23e474..571be140cc83f 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -191,8 +191,10 @@ template <typename ArrayRefT> class BytesView {
bool IsLittleEndian;
public:
+ explicit BytesView(ArrayRefT Ref, bool IsLittleEndian)
+ : Bytes(Ref), IsLittleEndian(IsLittleEndian) {}
explicit BytesView(ArrayRefT Ref, const DataLayout &DL)
- : Bytes(Ref), IsLittleEndian(DL.isLittleEndian()) {}
+ : BytesView(Ref, DL.isLittleEndian()) {}
auto &operator[](uint32_t Index) {
return Bytes[IsLittleEndian ? Index : Bytes.size() - 1 - Index];
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 120bc21885e37..d36220c446ebf 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -297,7 +297,8 @@ AnyValue AnyValue::getNullValue(Context &Ctx, Type *Ty) {
if (Ty->isPointerTy())
return Pointer::null(Ty->getPointerAddressSpace(), Ctx.getDataLayout());
if (Ty->isByteTy())
- return ByteValue::zero(Ty->getByteBitWidth());
+ return ByteValue::zero(Ty->getByteBitWidth(),
+ Ctx.getDataLayout().isLittleEndian());
if (auto *VecTy = dyn_cast<VectorType>(Ty)) {
uint32_t NumElements = Ctx.getEVL(VecTy->getElementCount());
return AnyValue(std::vector<AnyValue>(
@@ -323,10 +324,18 @@ AnyValue AnyValue::getVectorSplat(const AnyValue &Scalar, size_t NumElements) {
return AnyValue(std::vector<AnyValue>(NumElements, Scalar));
}
-ByteValue::ByteValue(const APInt &V, bool IsLittleEndian) {}
-ByteValue ByteValue::zero(uint32_t BitWidth) {
+ByteValue::ByteValue(const APInt &V, bool IsLittleEndian)
+ : BitWidth(V.getBitWidth()), IsLittleEndian(IsLittleEndian) {
+ Val.resize(divideCeil(BitWidth, 8));
+ MutableBytesView View(Val, IsLittleEndian);
+ for (uint8_t I = 0; I < BitWidth; I += 8)
+ View[I / 8] = Byte::concrete(static_cast<uint8_t>(
+ V.extractBitsAsZExtValue(std::min(BitWidth - I, 8), I)));
+}
+ByteValue ByteValue::zero(uint32_t BitWidth, bool IsLittleEndian) {
return ByteValue(
- BitWidth, std::vector<Byte>(divideCeil(BitWidth, 8), Byte::concrete(0)));
+ BitWidth, std::vector<Byte>(divideCeil(BitWidth, 8), Byte::concrete(0)),
+ IsLittleEndian);
}
ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) {
@@ -339,22 +348,26 @@ ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) {
else
Val.front().zeroBits(Mask);
}
- return ByteValue(BitWidth, std::move(Val));
+ return ByteValue(BitWidth, std::move(Val), IsLittleEndian);
}
void ByteValue::print(raw_ostream &OS) const {
OS << 'b' << BitWidth << ' ';
for (const Byte &V : Val) {
+ bool IsFullByte = (BitWidth & 7) == 0 ||
+ (IsLittleEndian ? &Val.back() : &Val.front()) != &V;
// Try to print a byte in short form
- if (V.ConcreteMask == 255 && V.TagMask == 0) {
+ if (IsFullByte && V.ConcreteMask == 255 && V.TagMask == 0) {
// Concrete value without provenance.
OS << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15);
- } else if (V.ConcreteMask == 0 && (V.Value == 0 || V.Value == 255)) {
+ } else if (IsFullByte && V.ConcreteMask == 0 &&
+ (V.Value == 0 || V.Value == 255)) {
// Poison/undef bytes.
OS << (V.Value == 0 ? "!!" : "??");
} else {
- for (uint32_t I = 0; I != 8; ++I) {
- uint32_t Mask = 1U << (8 - I);
+ uint32_t BitEnd = IsFullByte ? 8 : BitWidth & 7;
+ for (uint32_t I = 0; I != BitEnd; ++I) {
+ uint32_t Mask = 1U << (BitEnd - 1 - I);
if (V.ConcreteMask & Mask)
OS << (V.Value & Mask ? '1' : '0');
else
@@ -363,8 +376,8 @@ void ByteValue::print(raw_ostream &OS) const {
if (uint32_t TagMask = V.ConcreteMask & V.TagMask) {
// Print tags if available.
OS << '(';
- for (uint32_t I = 0; I != 8; ++I) {
- uint32_t Mask = 1U << (8 - I);
+ for (uint32_t I = 0; I != BitEnd; ++I) {
+ uint32_t Mask = 1U << (BitEnd - 1 - I);
if (TagMask & Mask)
OS << (V.TagValue & Mask ? '1' : '0');
else
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 96d01cf3d1909..039d870b24a5e 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -53,6 +53,7 @@ struct Byte {
void zeroBits(uint8_t Mask) {
ConcreteMask |= Mask;
Value &= ~Mask;
+ TagMask &= ~Mask;
}
void poisonBits(uint8_t Mask) {
@@ -113,6 +114,12 @@ struct Byte {
static_cast<uint8_t>(TagMask << Shift),
static_cast<uint8_t>(TagValue << Shift)};
}
+
+ bool AreHighBitsZExtd(uint8_t BitsFrom) const {
+ uint8_t Mask = static_cast<uint8_t>((~0U) << BitsFrom);
+ return (ConcreteMask & Mask) == Mask && (Value & Mask) == 0 &&
+ (TagMask & Mask) == 0;
+ }
};
enum class StorageKind {
@@ -237,24 +244,41 @@ class Pointer {
/// Represents a scalar byte value. If the value is not byte-sized, the high
/// bits are zero-padded.
class ByteValue {
+ // The byte order is endianness-dependent.
std::vector<Byte> Val;
- uint32_t BitWidth;
+ uint32_t BitWidth : 31;
+ uint32_t IsLittleEndian : 1;
public:
ByteValue(const APInt &V, bool IsLittleEndian);
- /// The caller is responsible to zero high bits for non-byte-sized values.
- ByteValue(uint32_t BitWidth, ArrayRef<Byte> Val)
- : Val(Val), BitWidth(BitWidth) {}
- /// The caller is responsible to zero high bits for non-byte-sized values.
- ByteValue(uint32_t BitWidth, std::vector<Byte> Val)
- : Val(std::move(Val)), BitWidth(BitWidth) {}
+ ByteValue(uint32_t BitWidth, ArrayRef<Byte> Val, bool IsLittleEndian,
+ bool ImplicitClearHighBits = false)
+ : ByteValue(BitWidth, std::vector<Byte>(Val), IsLittleEndian,
+ ImplicitClearHighBits) {}
+ ByteValue(uint32_t BitWidth, std::vector<Byte> Val, bool IsLittleEndian,
+ bool ImplicitClearHighBits = false)
+ : Val(std::move(Val)), BitWidth(BitWidth),
+ IsLittleEndian(IsLittleEndian) {
+ if (ImplicitClearHighBits && (BitWidth & 7) != 0) {
+ uint8_t Mask = static_cast<uint8_t>((~0U) << (BitWidth & 7));
+ if (IsLittleEndian)
+ this->Val.back().zeroBits(Mask);
+ else
+ this->Val.front().zeroBits(Mask);
+ }
+ assert(((BitWidth & 7) == 0 ||
+ ((IsLittleEndian ? this->Val.back() : this->Val.front())
+ .AreHighBitsZExtd(BitWidth & 7))) &&
+ "The caller is responsible to zero high bits for non-byte-sized "
+ "values.");
+ }
ByteValue(const ByteValue &) = default;
ByteValue(ByteValue &&) = default;
ByteValue &operator=(const ByteValue &) = default;
ByteValue &operator=(ByteValue &&) = default;
~ByteValue() = default;
- static ByteValue zero(uint32_t BitWidth);
+ static ByteValue zero(uint32_t BitWidth, bool IsLittleEndian);
static ByteValue poison(uint32_t BitWidth, bool IsLittleEndian);
uint32_t getBitWidth() const { return BitWidth; }
>From d8d5a56518f14fb3e0f8f9bb848b7940c23f1dc5 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 08:47:12 +0800
Subject: [PATCH 03/21] [llubi] Add tests. NFC.
---
llvm/test/tools/llubi/loadstore_be.ll | 69 +++++++++++++++++++++++++++
llvm/test/tools/llubi/loadstore_le.ll | 69 +++++++++++++++++++++++++++
2 files changed, 138 insertions(+)
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index 8f1f851ff13c2..b15bef03fdcfa 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -113,12 +113,47 @@ define void @main() {
%alloc_ptr = alloca ptr
store ptr %alloc_ptr, ptr %alloc_ptr
+ %bytes = load b64, ptr %alloc_ptr
; It should recover the provenance.
%ptr_with_provenance = load ptr, ptr %alloc_ptr
%addr_bits = load i8, ptr %alloc_ptr
store i8 %addr_bits, ptr %alloc_ptr
; The first byte is tainted. We cannot recover the provenance.
%ptr_without_provenance = load ptr, ptr %alloc_ptr
+ store b64 %bytes, ptr %alloc_ptr
+ ; It should recover the provenance.
+ %ptr_with_provenance2 = load ptr, ptr %alloc_ptr
+ %bv64b1 = bitcast b64 %bytes to <64 x b1>
+ %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse(<64 x b1> %bv64b1)
+ store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr
+ ; The bit order is incorrect. We cannot recover the provenance.
+ %ptr_without_provenance2 = load ptr, ptr %alloc_ptr
+ store b64 %bytes, ptr %alloc_ptr
+ store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr
+ %bytes_tainted = load b64, ptr %alloc_ptr
+ ; The first byte is tainted. We cannot recover the provenance.
+ %ptr_without_provenance3 = load ptr, ptr %alloc_ptr
+ %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8>
+ %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7
+ %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1>
+ %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse(<8 x b1> %first_byte_with_provenance_reversed_v8b1)
+ store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr
+ %bytes_recovered = load b64, ptr %alloc_ptr
+ ; It should recover the provenance.
+ %ptr_with_provenance3 = load ptr, ptr %alloc_ptr
+
+ %alloc_byte = alloca b32
+ store b8 127, ptr %alloc_byte
+ %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1
+ store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte
+ %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2
+ store b8 poison, ptr %gep_third_byte
+ %bytes_mixed = load b32, ptr %alloc_byte
+ store b32 u0xDEADBEEF, ptr %alloc_byte
+ %bytes_endianness = load b32, ptr %alloc_byte
+ %bytes_non_pow2 = load b28, ptr %alloc_byte
+ store b28 u0xEADBEEF, ptr %alloc_byte
+ %bytes_zextd = load b32, ptr %alloc_byte
ret void
}
@@ -208,5 +243,39 @@ define void @main() {
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [nullary]
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
+; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
+; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [nullary]
+; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x900000000000000 [nullary]
+; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
+; CHECK-NEXT: store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %bytes_tainted = load b64, ptr %alloc_ptr, align 8 => b64 0000!!!! 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
+; CHECK-NEXT: %ptr_without_provenance3 = load ptr, ptr %alloc_ptr, align 8 => poison
+; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00001001(00110100) , b8 00000000(00010000) , b8 00000000(10101001) , b8 00000000(01010001) , b8 00000000(10011100) , b8 00000000(00111100) , b8 00000000(10001110) , b8 00000000(01100100) }
+; CHECK-NEXT: %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7 => b8 00000000(01100100)
+; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) }
+; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
+; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0x98 [alloc_byte]
+; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
+; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0x99 [alloc_byte + 1]
+; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
+; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
+; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 7F 00000000(00100110) !! ??
+; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
+; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 DE AD BE EF
+; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 1110 AD BE EF
+; CHECK-NEXT: store b28 -22167825, ptr %alloc_byte, align 4
+; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0E AD BE EF
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index 9ad92ac6e75e0..4e6348a6eed75 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -114,12 +114,47 @@ define void @main() {
%alloc_ptr = alloca ptr
store ptr %alloc_ptr, ptr %alloc_ptr
+ %bytes = load b64, ptr %alloc_ptr
; It should recover the provenance.
%ptr_with_provenance = load ptr, ptr %alloc_ptr
%addr_bits = load i8, ptr %alloc_ptr
store i8 %addr_bits, ptr %alloc_ptr
; The first byte is tainted. We cannot recover the provenance.
%ptr_without_provenance = load ptr, ptr %alloc_ptr
+ store b64 %bytes, ptr %alloc_ptr
+ ; It should recover the provenance.
+ %ptr_with_provenance2 = load ptr, ptr %alloc_ptr
+ %bv64b1 = bitcast b64 %bytes to <64 x b1>
+ %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse(<64 x b1> %bv64b1)
+ store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr
+ ; The bit order is incorrect. We cannot recover the provenance.
+ %ptr_without_provenance2 = load ptr, ptr %alloc_ptr
+ store b64 %bytes, ptr %alloc_ptr
+ store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr
+ %bytes_tainted = load b64, ptr %alloc_ptr
+ ; The first byte is tainted. We cannot recover the provenance.
+ %ptr_without_provenance3 = load ptr, ptr %alloc_ptr
+ %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8>
+ %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7
+ %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1>
+ %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse(<8 x b1> %first_byte_with_provenance_reversed_v8b1)
+ store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr
+ %bytes_recovered = load b64, ptr %alloc_ptr
+ ; It should recover the provenance.
+ %ptr_with_provenance3 = load ptr, ptr %alloc_ptr
+
+ %alloc_byte = alloca b32
+ store b8 127, ptr %alloc_byte
+ %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1
+ store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte
+ %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2
+ store b8 poison, ptr %gep_third_byte
+ %bytes_mixed = load b32, ptr %alloc_byte
+ store b32 u0xDEADBEEF, ptr %alloc_byte
+ %bytes_endianness = load b32, ptr %alloc_byte
+ %bytes_non_pow2 = load b28, ptr %alloc_byte
+ store b28 u0xEADBEEF, ptr %alloc_byte
+ %bytes_zextd = load b32, ptr %alloc_byte
ret void
}
@@ -210,5 +245,39 @@ define void @main() {
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -64
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [nullary]
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 10010000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -112
+; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [nullary]
+; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x900000000000000 [nullary]
+; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
+; CHECK-NEXT: store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %bytes_tainted = load b64, ptr %alloc_ptr, align 8 => b64 !!!!0000 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %ptr_without_provenance3 = load ptr, ptr %alloc_ptr, align 8 => poison
+; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00000000(01100100) , b8 00000000(10001110) , b8 00000000(00111100) , b8 00000000(10011100) , b8 00000000(01010001) , b8 00000000(10101001) , b8 00000000(00010000) , b8 00001001(00110100) }
+; CHECK-NEXT: %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7 => b8 00001001(00110100)
+; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) }
+; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 10010000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
+; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0x98 [alloc_byte]
+; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
+; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0x99 [alloc_byte + 1]
+; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
+; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
+; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 7F 10010000(00101100) !! ??
+; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
+; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 EF BE AD DE
+; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 EF BE AD 1110
+; CHECK-NEXT: store b28 -22167825, ptr %alloc_byte, align 4
+; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 EF BE AD 0E
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
>From 9855f6600ca5c801af6f7b7a01409bbd3c6234c9 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 08:56:44 +0800
Subject: [PATCH 04/21] [llubi] Add tests with !noundef check. NFC.
---
llvm/test/tools/llubi/load_noundef_ub_undef.ll | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/test/tools/llubi/load_noundef_ub_undef.ll b/llvm/test/tools/llubi/load_noundef_ub_undef.ll
index bb9f5c79cf260..fdc94b602c7a2 100644
--- a/llvm/test/tools/llubi/load_noundef_ub_undef.ll
+++ b/llvm/test/tools/llubi/load_noundef_ub_undef.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+; RUN: sed 's/i32/b32/g' %s | not llubi --verbose 2>&1 | FileCheck %s
define void @main() {
%p = alloca i32
@@ -7,8 +8,8 @@ define void @main() {
ret void
}
; CHECK: Entering function: main
-; CHECK-NEXT: %p = alloca i32, align 4 => ptr 0x8 [p]
+; CHECK-NEXT: %p = alloca {{i|b}}32, align 4 => ptr 0x8 [p]
; CHECK-NEXT: Stacktrace:
-; CHECK-NEXT: #0 %res = load i32, ptr %p, align 4, !noundef !0 at @main <stdin>:6
+; CHECK-NEXT: #0 %res = load {{i|b}}32, ptr %p, align 4, !noundef !0 at @main <stdin>:7
; CHECK-NEXT: Immediate UB detected: The value loaded contains undefined bits.
; CHECK-NEXT: error: Execution of function 'main' failed.
>From 7b40a4b55fb48e7d1af34ee9f64ca509b56643b5 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 09:11:17 +0800
Subject: [PATCH 05/21] [llubi] Handle noundef attribute for byte types
---
llvm/test/tools/llubi/attribute_noundef_ub.ll | 5 +++--
llvm/test/tools/llubi/attributes.ll | 11 +++++++++++
llvm/tools/llubi/lib/Interpreter.cpp | 14 ++++++++++++--
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/llvm/test/tools/llubi/attribute_noundef_ub.ll b/llvm/test/tools/llubi/attribute_noundef_ub.ll
index a8a6a1984398b..f2bd1d599b7b6 100644
--- a/llvm/test/tools/llubi/attribute_noundef_ub.ll
+++ b/llvm/test/tools/llubi/attribute_noundef_ub.ll
@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+; RUN: sed 's/i32/b32/g' %s | not llubi --verbose 2>&1 | FileCheck %s
define void @callee(i32 noundef %x) {
ret void
@@ -11,6 +12,6 @@ define void @main() {
}
; CHECK: Entering function: main
; CHECK-NEXT: Stacktrace:
-; CHECK-NEXT: #0 call void @callee(i32 poison) at @main <stdin>:9
-; CHECK-NEXT: Immediate UB detected: The value poison violates noundef attribute.
+; CHECK-NEXT: #0 call void @callee({{i|b}}32 poison) at @main <stdin>:10
+; CHECK-NEXT: Immediate UB detected: The value {{poison|b32 !! !! !! !!}} violates noundef attribute.
; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/attributes.ll b/llvm/test/tools/llubi/attributes.ll
index 87057ab816ba7..931924b443643 100644
--- a/llvm/test/tools/llubi/attributes.ll
+++ b/llvm/test/tools/llubi/attributes.ll
@@ -47,6 +47,10 @@ define noundef i32 @identity_noundef(i32 noundef %x) {
ret i32 %x
}
+define noundef b7 @identity_noundef_byte(b7 noundef %x) {
+ ret b7 %x
+}
+
define noundef {i32, <2 x i32>, [2 x i32]} @identity_noundef_agg({i32, <2 x i32>, [2 x i32]} noundef %x) {
ret {i32, <2 x i32>, [2 x i32]} %x
}
@@ -119,6 +123,8 @@ define void @main() {
%fmt_n_out = alloca [6 x i8]
store [6 x i8] c"N=%d\0A\00", ptr %fmt_n_out
%res = call range(i32 0, 15) noundef i32 (ptr, ...) @printf(ptr noundef nonnull %fmt_n_out, i32 noundef range(i32 0, 15) 6)
+
+ %noundef_byte = call b7 @identity_noundef_byte(b7 -1)
ret void
}
; CHECK: Entering function: main
@@ -324,5 +330,10 @@ define void @main() {
; CHECK-NEXT: store [6 x i8] c"N=%d\0A\00", ptr %fmt_n_out, align 1
; CHECK-NEXT: N=6
; CHECK-NEXT: %res = call noundef range(i32 0, 15) i32 (ptr, ...) @printf(ptr noundef nonnull %fmt_n_out, i32 noundef range(i32 0, 15) 6) => i32 4
+; CHECK-NEXT: Entering function: identity_noundef_byte
+; CHECK-NEXT: b7 %x = b7 1111111
+; CHECK-NEXT: ret b7 %x
+; CHECK-NEXT: Exiting function: identity_noundef_byte
+; CHECK-NEXT: %noundef_byte = call b7 @identity_noundef_byte(b7 -1) => b7 1111111
; 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 425215b4aee2e..ff6f57a875ca9 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -78,8 +78,18 @@ static void applyAlignAttr(AnyValue &V, Align Alignment) {
static bool violatesNoUndefAttr(AnyValue &V) {
bool ContainsPoison = false;
- forEachScalarValue(
- V, [&](AnyValue &Scalar) { ContainsPoison |= Scalar.isPoison(); });
+ forEachScalarValue(V, [&](AnyValue &Scalar) {
+ if (Scalar.isPoison()) {
+ ContainsPoison = true;
+ return;
+ }
+ if (Scalar.isByte() && !ContainsPoison) {
+ // For non-byte-sized values, high bits are always zeroed out.
+ ContainsPoison = any_of(Scalar.asByte().bytes(), [](const Byte &V) {
+ return V.ConcreteMask != 255;
+ });
+ }
+ });
return ContainsPoison;
}
>From d14d4d17ac9edc0408e751550cc9fa05551551ec Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 09:17:06 +0800
Subject: [PATCH 06/21] [llubi] Use ImplicitClearHighBits in
`ByteValue::poison`
---
llvm/tools/llubi/lib/Value.cpp | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index d36220c446ebf..13ea3a7b4fd1e 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -339,16 +339,9 @@ ByteValue ByteValue::zero(uint32_t BitWidth, bool IsLittleEndian) {
}
ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) {
- std::vector<Byte> Val(divideCeil(BitWidth, 8), Byte::poison());
- // Zero high-bits if it is not a byte-sized value.
- if (BitWidth & 7) {
- uint8_t Mask = static_cast<uint8_t>((~0U) << (BitWidth & 7));
- if (IsLittleEndian)
- Val.back().zeroBits(Mask);
- else
- Val.front().zeroBits(Mask);
- }
- return ByteValue(BitWidth, std::move(Val), IsLittleEndian);
+ return ByteValue(BitWidth,
+ std::vector<Byte>(divideCeil(BitWidth, 8), Byte::poison()),
+ IsLittleEndian, /*ImplicitClearHighBits=*/true);
}
void ByteValue::print(raw_ostream &OS) const {
>From c38d08fab05ab4fadf5dff4afc3a0707936d9cfb Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 21:02:21 +0800
Subject: [PATCH 07/21] [llubi] Use uint32_t
---
llvm/tools/llubi/lib/Value.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 13ea3a7b4fd1e..c5898e45dd0a0 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -328,7 +328,7 @@ ByteValue::ByteValue(const APInt &V, bool IsLittleEndian)
: BitWidth(V.getBitWidth()), IsLittleEndian(IsLittleEndian) {
Val.resize(divideCeil(BitWidth, 8));
MutableBytesView View(Val, IsLittleEndian);
- for (uint8_t I = 0; I < BitWidth; I += 8)
+ for (uint32_t I = 0; I < BitWidth; I += 8)
View[I / 8] = Byte::concrete(static_cast<uint8_t>(
V.extractBitsAsZExtValue(std::min(BitWidth - I, 8), I)));
}
>From 3a3266df0c9553eb22ad67b49b8ef5effefa024a Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 1 Jun 2026 22:14:10 +0800
Subject: [PATCH 08/21] [llubi] Fix compilation error.
---
llvm/tools/llubi/lib/Value.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index c5898e45dd0a0..5a6f18b9a93fa 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -330,7 +330,7 @@ ByteValue::ByteValue(const APInt &V, bool IsLittleEndian)
MutableBytesView View(Val, IsLittleEndian);
for (uint32_t I = 0; I < BitWidth; I += 8)
View[I / 8] = Byte::concrete(static_cast<uint8_t>(
- V.extractBitsAsZExtValue(std::min(BitWidth - I, 8), I)));
+ V.extractBitsAsZExtValue(std::min(BitWidth - I, 8U), I)));
}
ByteValue ByteValue::zero(uint32_t BitWidth, bool IsLittleEndian) {
return ByteValue(
>From 61919bee4b101e401f897098fa217366b1dbb0d5 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 2 Jun 2026 01:08:44 +0800
Subject: [PATCH 09/21] [llubi] Address review comments.
---
llvm/test/tools/llubi/attribute_noundef_ub.ll | 2 +-
llvm/test/tools/llubi/loadstore_be.ll | 12 ++++++++----
llvm/test/tools/llubi/loadstore_le.ll | 12 ++++++++----
llvm/tools/llubi/lib/Context.cpp | 8 ++++++--
llvm/tools/llubi/lib/Value.cpp | 4 ++--
llvm/tools/llubi/lib/Value.h | 2 ++
6 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/llvm/test/tools/llubi/attribute_noundef_ub.ll b/llvm/test/tools/llubi/attribute_noundef_ub.ll
index f2bd1d599b7b6..cab5e22df8d0a 100644
--- a/llvm/test/tools/llubi/attribute_noundef_ub.ll
+++ b/llvm/test/tools/llubi/attribute_noundef_ub.ll
@@ -13,5 +13,5 @@ define void @main() {
; CHECK: Entering function: main
; CHECK-NEXT: Stacktrace:
; CHECK-NEXT: #0 call void @callee({{i|b}}32 poison) at @main <stdin>:10
-; CHECK-NEXT: Immediate UB detected: The value {{poison|b32 !! !! !! !!}} violates noundef attribute.
+; CHECK-NEXT: Immediate UB detected: The value {{poison|b32 0x!! 0x!! 0x!! 0x!!}} violates noundef attribute.
; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index b15bef03fdcfa..6bf7050603546 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -154,6 +154,8 @@ define void @main() {
%bytes_non_pow2 = load b28, ptr %alloc_byte
store b28 u0xEADBEEF, ptr %alloc_byte
%bytes_zextd = load b32, ptr %alloc_byte
+ store b8 255, ptr %alloc_byte
+ %bytes_zextd_poison = load b7, ptr %alloc_byte
ret void
}
@@ -271,11 +273,13 @@ define void @main() {
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
-; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 7F 00000000(00100110) !! ??
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 00000000(00100110) 0x!! 0x??
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
-; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 DE AD BE EF
-; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 1110 AD BE EF
+; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 0xDE 0xAD 0xBE 0xEF
+; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 !!!! 0x!! 0x!! 0x!!
; CHECK-NEXT: store b28 -22167825, ptr %alloc_byte, align 4
-; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0E AD BE EF
+; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0x0E 0xAD 0xBE 0xEF
+; CHECK-NEXT: store b8 -1, ptr %alloc_byte, align 1
+; CHECK-NEXT: %bytes_zextd_poison = load b7, ptr %alloc_byte, align 1 => b7 !!!!!!!
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index 4e6348a6eed75..d31555fedb5b5 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -155,6 +155,8 @@ define void @main() {
%bytes_non_pow2 = load b28, ptr %alloc_byte
store b28 u0xEADBEEF, ptr %alloc_byte
%bytes_zextd = load b32, ptr %alloc_byte
+ store b8 255, ptr %alloc_byte
+ %bytes_zextd_poison = load b7, ptr %alloc_byte
ret void
}
@@ -273,11 +275,13 @@ define void @main() {
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
-; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 7F 10010000(00101100) !! ??
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 10010000(00101100) 0x!! 0x??
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
-; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 EF BE AD DE
-; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 EF BE AD 1110
+; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 0xEF 0xBE 0xAD 0xDE
+; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 0x!! 0x!! 0x!! !!!!
; CHECK-NEXT: store b28 -22167825, ptr %alloc_byte, align 4
-; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 EF BE AD 0E
+; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0xEF 0xBE 0xAD 0x0E
+; CHECK-NEXT: store b8 -1, ptr %alloc_byte, align 1
+; CHECK-NEXT: %bytes_zextd_poison = load b7, ptr %alloc_byte, align 1 => b7 !!!!!!!
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index b18faa017369d..a0097878b5062 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -573,8 +573,12 @@ AnyValue Context::fromBytes(ArrayRef<Byte> Bytes, Type *Ty,
}
}
unsigned BitWidth = Ty->getByteBitWidth();
- ByteValue Res(BitWidth, Bytes, DL.isLittleEndian(),
- /*ImplicitClearHighBits=*/true);
+ if (BitWidth & 7) {
+ if (!(DL.isLittleEndian() ? Bytes.back() : Bytes.front())
+ .AreHighBitsZExtd(BitWidth & 7))
+ return ByteValue::poison(BitWidth, DL.isLittleEndian());
+ }
+ ByteValue Res(BitWidth, Bytes, DL.isLittleEndian());
return AnyValue(std::move(Res));
}
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 5a6f18b9a93fa..80c27077e4e2c 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -352,11 +352,11 @@ void ByteValue::print(raw_ostream &OS) const {
// Try to print a byte in short form
if (IsFullByte && V.ConcreteMask == 255 && V.TagMask == 0) {
// Concrete value without provenance.
- OS << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15);
+ OS << "0x" << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15);
} else if (IsFullByte && V.ConcreteMask == 0 &&
(V.Value == 0 || V.Value == 255)) {
// Poison/undef bytes.
- OS << (V.Value == 0 ? "!!" : "??");
+ OS << (V.Value == 0 ? "0x!!" : "0x??");
} else {
uint32_t BitEnd = IsFullByte ? 8 : BitWidth & 7;
for (uint32_t I = 0; I != BitEnd; ++I) {
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 039d870b24a5e..9e30c2cda1fc3 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -345,6 +345,8 @@ class [[nodiscard]] AnyValue {
return Ty->isFloatingPointTy();
case StorageKind::Pointer:
return Ty->isPointerTy();
+ case StorageKind::Byte:
+ return Ty->isByteTy();
// We don't check elements recursively.
case StorageKind::Aggregate:
return Ty->isAggregateType() || Ty->isVectorTy();
>From c1330fac74357aed78b220c453be7d0dbb564419 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 2 Jun 2026 01:14:42 +0800
Subject: [PATCH 10/21] [llubi] Add freeze tests. NFC.
---
llvm/test/tools/llubi/freeze.ll | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/llvm/test/tools/llubi/freeze.ll b/llvm/test/tools/llubi/freeze.ll
index c63db99b08084..6140136509c6b 100644
--- a/llvm/test/tools/llubi/freeze.ll
+++ b/llvm/test/tools/llubi/freeze.ll
@@ -16,6 +16,15 @@ define void @main() {
%arr_freeze = freeze [2 x i32] [i32 10, i32 poison]
%struct_freeze = freeze { i32, float } { i32 10, float poison }
%struct_freeze_nested = freeze { i32, { float, ptr } } { i32 poison, { float, ptr } { float 10.0, ptr poison } }
+
+ %alloca = alloca ptr
+ %byte_undef = load b8, ptr %alloca
+ store ptr %alloca, ptr %alloca
+ %byte_with_provenance = load b8, ptr %alloca
+ %vec_bytes1 = insertelement <4 x b8> <b8 0, b8 poison, b8 poison, b8 poison>, b8 %byte_undef, i32 2
+ %vec_bytes2 = insertelement <4 x b8> %vec_bytes1, b8 %byte_with_provenance, i32 3
+ %bytes = bitcast <4 x b8> %vec_bytes2 to b32
+ %bytes_freeze = freeze b32 %bytes
ret void
}
; CHECK: Entering function: main
@@ -30,5 +39,13 @@ define void @main() {
; CHECK-NEXT: %arr_freeze = freeze [2 x i32] [i32 10, i32 poison] => { i32 10, i32 -44045842 }
; CHECK-NEXT: %struct_freeze = freeze { i32, float } { i32 10, float poison } => { i32 10, float 0x042AE12F }
; CHECK-NEXT: %struct_freeze_nested = freeze { i32, { float, ptr } } { i32 poison, { float, ptr } { float 1.000000e+01, ptr poison } } => { i32 1166165736, { float 1.000000e+01, ptr 0xD79E62976F604366 [nullary] } }
+; CHECK-NEXT: %alloca = alloca ptr, align 8 => ptr 0x8 [alloca]
+; CHECK-NEXT: %byte_undef = load b8, ptr %alloca, align 1 => b8 0x??
+; CHECK-NEXT: store ptr %alloca, ptr %alloca, align 8
+; CHECK-NEXT: %byte_with_provenance = load b8, ptr %alloca, align 1 => b8 00001000(00011001)
+; CHECK-NEXT: %vec_bytes1 = insertelement <4 x b8> <b8 0, b8 poison, b8 poison, b8 poison>, b8 %byte_undef, i32 2 => { b8 0x00 , b8 0x!! , b8 0x?? , b8 0x!! }
+; CHECK-NEXT: %vec_bytes2 = insertelement <4 x b8> %vec_bytes1, b8 %byte_with_provenance, i32 3 => { b8 0x00 , b8 0x!! , b8 0x?? , b8 00001000(00011001) }
+; CHECK-NEXT: %bytes = bitcast <4 x b8> %vec_bytes2 to b32 => b32 0x00 0x!! 0x?? 00001000(00011001)
+; CHECK-NEXT: %bytes_freeze = freeze b32 %bytes => b32 0x00 0x13 0xF8 00001000(00011001)
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
>From 96b62512dff63d54f16ecae7bb760113198e4160 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 2 Jun 2026 22:41:14 +0800
Subject: [PATCH 11/21] [llubi] Add global tests with byte type
---
llvm/test/tools/llubi/global.ll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/test/tools/llubi/global.ll b/llvm/test/tools/llubi/global.ll
index 3cff71b21769f..c4f8c734a1a6b 100644
--- a/llvm/test/tools/llubi/global.ll
+++ b/llvm/test/tools/llubi/global.ll
@@ -1,8 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
target datalayout = "e-p:64:64:64-i32:32:32"
@value = global i32 41
+ at value_byte = global b8 1
@value_ptr = global ptr @value
@aggregate = global { i32, [2 x i8] } { i32 7, [2 x i8] c"hi" }
@@ -12,6 +14,7 @@ define void @main() {
%ptr = load ptr, ptr @value_ptr
%updated = load i32, ptr %ptr
%aggregate_value = load { i32, [2 x i8] }, ptr @aggregate
+ %val = load i8, ptr @value_byte
ret void
}
@@ -21,5 +24,6 @@ define void @main() {
; CHECK-NEXT: %ptr = load ptr, ptr @value_ptr, align 8 => ptr 0x8 [@value]
; CHECK-NEXT: %updated = load i32, ptr %ptr, align 4 => i32 42
; CHECK-NEXT: %aggregate_value = load { i32, [2 x i8] }, ptr @aggregate, align 4 => { i32 7, { i8 104, i8 105 } }
+; CHECK-NEXT: %val = load i8, ptr @value_byte, align 1 => i8 1
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
>From 3309122311998025372f4ea4e62684c1bad916bf Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 4 Jun 2026 01:55:35 +0800
Subject: [PATCH 12/21] [llubi] Add bitcast tests. NFC.
---
llvm/test/tools/llubi/bitcast_be.ll | 7 +++++++
llvm/test/tools/llubi/bitcast_le.ll | 7 +++++++
2 files changed, 14 insertions(+)
diff --git a/llvm/test/tools/llubi/bitcast_be.ll b/llvm/test/tools/llubi/bitcast_be.ll
index f5cd5d3c9dd24..9b962534c3052 100644
--- a/llvm/test/tools/llubi/bitcast_be.ll
+++ b/llvm/test/tools/llubi/bitcast_be.ll
@@ -29,6 +29,10 @@ entry:
%bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half>
%bitcast_floatvec2int = bitcast <4 x half> <half 1.0, half 2.0, half 3.0, half 4.0> to i64
+
+ %bitcast_ptr2b64 = bitcast ptr %ptr to b64
+ %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64
+ %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr
ret void
}
; CHECK: Entering function: main
@@ -54,5 +58,8 @@ entry:
; CHECK-NEXT: %bitcast_vec2vec_weird = bitcast <8 x i3> <i3 0, i3 1, i3 2, i3 3, i3 -4, i3 -3, i3 -2, i3 -1> to <3 x i8> => { i8 5, i8 57, i8 119 }
; CHECK-NEXT: %bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half> => { half 0.000000e+00, half 5.960460e-08, half 0.000000e+00, half 1.192090e-07 }
; CHECK-NEXT: %bitcast_floatvec2int = bitcast <4 x half> <half 1.000000e+00, half 2.000000e+00, half 3.000000e+00, half 4.000000e+00> to i64 => i64 4323526012127167488
+; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 00000000(00101000) 00000000(11101000) 00000000(00110111) 00000000(11000101) 00000000(11001011) 00000000(01000001) 00000000(11011100) 00001000(00111110)
+; CHECK-NEXT: %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64 => i64 8
+; CHECK-NEXT: %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr => ptr 0x8 [ptr]
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/bitcast_le.ll b/llvm/test/tools/llubi/bitcast_le.ll
index e949d6655aeb0..7151fc6364020 100644
--- a/llvm/test/tools/llubi/bitcast_le.ll
+++ b/llvm/test/tools/llubi/bitcast_le.ll
@@ -29,6 +29,10 @@ entry:
%bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half>
%bitcast_floatvec2int = bitcast <4 x half> <half 1.0, half 2.0, half 3.0, half 4.0> to i64
+
+ %bitcast_ptr2b64 = bitcast ptr %ptr to b64
+ %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64
+ %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr
ret void
}
; CHECK: Entering function: main
@@ -54,5 +58,8 @@ entry:
; CHECK-NEXT: %bitcast_vec2vec_weird = bitcast <8 x i3> <i3 0, i3 1, i3 2, i3 3, i3 -4, i3 -3, i3 -2, i3 -1> to <3 x i8> => { i8 -120, i8 -58, i8 -6 }
; CHECK-NEXT: %bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half> => { half 5.960460e-08, half 0.000000e+00, half 1.192090e-07, half 0.000000e+00 }
; CHECK-NEXT: %bitcast_floatvec2int = bitcast <4 x half> <half 1.000000e+00, half 2.000000e+00, half 3.000000e+00, half 4.000000e+00> to i64 => i64 4899988963420290048
+; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 00001000(00111110) 00000000(11011100) 00000000(01000001) 00000000(11001011) 00000000(11000101) 00000000(00110111) 00000000(11101000) 00000000(00101000)
+; CHECK-NEXT: %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64 => i64 8
+; CHECK-NEXT: %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr => ptr 0x8 [ptr]
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
>From 85d6f342719b8d3b94bc582d725cdd7e2191df6b Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 16 Jun 2026 23:45:41 +0800
Subject: [PATCH 13/21] [llubi] Update check lines after rebasing
---
llvm/test/tools/llubi/loadstore_be.ll | 28 ++++++++++------------
llvm/test/tools/llubi/loadstore_le.ll | 34 ++++++++++++---------------
2 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index 6bf7050603546..d8de9fbe32968 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -241,37 +241,33 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 11000000(00101100)
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [nullary]
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
-; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
-; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [nullary]
; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
-; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 1(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(1) , b1 1(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x900000000000000 [nullary]
+; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x300000000000000 [nullary]
; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
; CHECK-NEXT: store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_tainted = load b64, ptr %alloc_ptr, align 8 => b64 0000!!!! 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
+; CHECK-NEXT: %bytes_tainted = load b64, ptr %alloc_ptr, align 8 => b64 0000!!!! 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 11000000(00101100)
; CHECK-NEXT: %ptr_without_provenance3 = load ptr, ptr %alloc_ptr, align 8 => poison
-; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00001001(00110100) , b8 00000000(00010000) , b8 00000000(10101001) , b8 00000000(01010001) , b8 00000000(10011100) , b8 00000000(00111100) , b8 00000000(10001110) , b8 00000000(01100100) }
+; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00000011(00110100) , b8 00000000(00010000) , b8 00000000(10101001) , b8 00000000(01010001) , b8 00000000(10011100) , b8 00000000(00111100) , b8 00000000(10001110) , b8 00000000(01100100) }
; CHECK-NEXT: %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7 => b8 00000000(01100100)
; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 10010000(00101100)
-; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0x98 [alloc_byte]
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 11000000(00101100)
+; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
-; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0x99 [alloc_byte + 1]
+; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0xCD [alloc_byte + 1]
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
-; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
+; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0xCE [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 00000000(00100110) 0x!! 0x??
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index d31555fedb5b5..13cd6a415bee8 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -243,39 +243,35 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 11000000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -64
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [nullary]
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 10010000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
-; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -112
-; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %ptr_without_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [nullary]
; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
-; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %ptr_with_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bv64b1 = bitcast b64 %bytes to <64 x b1> => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(1) , b1 1(0) , b1 1(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %bv64b1_reversed = call <64 x b1> @llvm.vector.reverse.v64b1(<64 x b1> %bv64b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(0) , b1 1(0) , b1 1(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: store <64 x b1> %bv64b1_reversed, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x900000000000000 [nullary]
+; CHECK-NEXT: %ptr_without_provenance2 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x300000000000000 [nullary]
; CHECK-NEXT: store b64 %bytes, ptr %alloc_ptr, align 8
; CHECK-NEXT: store <2 x b4> <b4 0, b4 poison>, ptr %alloc_ptr, align 1
; CHECK-NEXT: %bytes_tainted = load b64, ptr %alloc_ptr, align 8 => b64 !!!!0000 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
; CHECK-NEXT: %ptr_without_provenance3 = load ptr, ptr %alloc_ptr, align 8 => poison
-; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00000000(01100100) , b8 00000000(10001110) , b8 00000000(00111100) , b8 00000000(10011100) , b8 00000000(01010001) , b8 00000000(10101001) , b8 00000000(00010000) , b8 00001001(00110100) }
-; CHECK-NEXT: %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7 => b8 00001001(00110100)
-; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 1(0) , b1 0(0) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
-; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 1(0) , b1 0(1) , b1 0(0) , b1 1(0) }
+; CHECK-NEXT: %bv8b8_reversed = bitcast <64 x b1> %bv64b1_reversed to <8 x b8> => { b8 00000000(01100100) , b8 00000000(10001110) , b8 00000000(00111100) , b8 00000000(10011100) , b8 00000000(01010001) , b8 00000000(10101001) , b8 00000000(00010000) , b8 00000011(00110100) }
+; CHECK-NEXT: %first_byte_with_provenance_reversed = extractelement <8 x b8> %bv8b8_reversed, i32 7 => b8 00000011(00110100)
+; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 1(0) , b1 1(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
+; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(1) , b1 1(0) , b1 1(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 10010000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
-; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0x90 [alloc_ptr]
-; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0x98 [alloc_byte]
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 11000000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
-; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0x99 [alloc_byte + 1]
+; CHECK-NEXT: %gep_second_byte = getelementptr i8, ptr %alloc_byte, i64 1 => ptr 0xCD [alloc_byte + 1]
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
-; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0x9A [alloc_byte + 2]
+; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0xCE [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
-; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 10010000(00101100) 0x!! 0x??
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 11000000(00101100) 0x!! 0x??
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 0xEF 0xBE 0xAD 0xDE
; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 0x!! 0x!! 0x!! !!!!
>From 262de684de08981c0b6b8bb26bd3e98213ee20d7 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 16 Jun 2026 23:51:08 +0800
Subject: [PATCH 14/21] [llubi] Pre-commit problematic cases. NFC.
---
llvm/test/tools/llubi/bytes_undef.ll | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 llvm/test/tools/llubi/bytes_undef.ll
diff --git a/llvm/test/tools/llubi/bytes_undef.ll b/llvm/test/tools/llubi/bytes_undef.ll
new file mode 100644
index 0000000000000..befda937f284c
--- /dev/null
+++ b/llvm/test/tools/llubi/bytes_undef.ll
@@ -0,0 +1,21 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+; byte values don't contain undef bits.
+
+define void @main() {
+ %undef = alloca b8
+ %load_undef1 = load b8, ptr %undef
+ %load_undef2 = load b8, ptr %undef
+ %bitcast_undef1 = bitcast b8 %load_undef1 to i8
+ %bitcast_undef2 = bitcast b8 %load_undef1 to i8
+ ret void
+}
+; CHECK: Entering function: main
+; CHECK-NEXT: %undef = alloca b8, align 1 => ptr 0x8 [undef]
+; CHECK-NEXT: %load_undef1 = load b8, ptr %undef, align 1 => b8 0x??
+; CHECK-NEXT: %load_undef2 = load b8, ptr %undef, align 1 => b8 0x??
+; CHECK-NEXT: %bitcast_undef1 = bitcast b8 %load_undef1 to i8 => i8 62
+; CHECK-NEXT: %bitcast_undef2 = bitcast b8 %load_undef1 to i8 => i8 -117
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
>From 7fb7c680761ce07b577f72f4752a9216975e68e4 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 17 Jun 2026 00:13:33 +0800
Subject: [PATCH 15/21] [llubi] Freeze undef bits in byte value
---
llvm/test/tools/llubi/bytes_undef.ll | 6 ++---
llvm/test/tools/llubi/freeze.ll | 12 +++++-----
llvm/test/tools/llubi/loadstore_be.ll | 2 +-
llvm/test/tools/llubi/loadstore_le.ll | 2 +-
llvm/tools/llubi/lib/Context.cpp | 33 +++++++++++++++++++--------
llvm/tools/llubi/lib/Value.cpp | 15 +++++++-----
6 files changed, 43 insertions(+), 27 deletions(-)
diff --git a/llvm/test/tools/llubi/bytes_undef.ll b/llvm/test/tools/llubi/bytes_undef.ll
index befda937f284c..6bf51ca69d2b1 100644
--- a/llvm/test/tools/llubi/bytes_undef.ll
+++ b/llvm/test/tools/llubi/bytes_undef.ll
@@ -13,9 +13,9 @@ define void @main() {
}
; CHECK: Entering function: main
; CHECK-NEXT: %undef = alloca b8, align 1 => ptr 0x8 [undef]
-; CHECK-NEXT: %load_undef1 = load b8, ptr %undef, align 1 => b8 0x??
-; CHECK-NEXT: %load_undef2 = load b8, ptr %undef, align 1 => b8 0x??
+; CHECK-NEXT: %load_undef1 = load b8, ptr %undef, align 1 => b8 0x3E
+; CHECK-NEXT: %load_undef2 = load b8, ptr %undef, align 1 => b8 0x8B
; CHECK-NEXT: %bitcast_undef1 = bitcast b8 %load_undef1 to i8 => i8 62
-; CHECK-NEXT: %bitcast_undef2 = bitcast b8 %load_undef1 to i8 => i8 -117
+; CHECK-NEXT: %bitcast_undef2 = bitcast b8 %load_undef1 to i8 => i8 62
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/freeze.ll b/llvm/test/tools/llubi/freeze.ll
index 6140136509c6b..c85a1458dd668 100644
--- a/llvm/test/tools/llubi/freeze.ll
+++ b/llvm/test/tools/llubi/freeze.ll
@@ -40,12 +40,12 @@ define void @main() {
; CHECK-NEXT: %struct_freeze = freeze { i32, float } { i32 10, float poison } => { i32 10, float 0x042AE12F }
; CHECK-NEXT: %struct_freeze_nested = freeze { i32, { float, ptr } } { i32 poison, { float, ptr } { float 1.000000e+01, ptr poison } } => { i32 1166165736, { float 1.000000e+01, ptr 0xD79E62976F604366 [nullary] } }
; CHECK-NEXT: %alloca = alloca ptr, align 8 => ptr 0x8 [alloca]
-; CHECK-NEXT: %byte_undef = load b8, ptr %alloca, align 1 => b8 0x??
+; CHECK-NEXT: %byte_undef = load b8, ptr %alloca, align 1 => b8 0x19
; CHECK-NEXT: store ptr %alloca, ptr %alloca, align 8
-; CHECK-NEXT: %byte_with_provenance = load b8, ptr %alloca, align 1 => b8 00001000(00011001)
-; CHECK-NEXT: %vec_bytes1 = insertelement <4 x b8> <b8 0, b8 poison, b8 poison, b8 poison>, b8 %byte_undef, i32 2 => { b8 0x00 , b8 0x!! , b8 0x?? , b8 0x!! }
-; CHECK-NEXT: %vec_bytes2 = insertelement <4 x b8> %vec_bytes1, b8 %byte_with_provenance, i32 3 => { b8 0x00 , b8 0x!! , b8 0x?? , b8 00001000(00011001) }
-; CHECK-NEXT: %bytes = bitcast <4 x b8> %vec_bytes2 to b32 => b32 0x00 0x!! 0x?? 00001000(00011001)
-; CHECK-NEXT: %bytes_freeze = freeze b32 %bytes => b32 0x00 0x13 0xF8 00001000(00011001)
+; CHECK-NEXT: %byte_with_provenance = load b8, ptr %alloca, align 1 => b8 00001000(00010011)
+; CHECK-NEXT: %vec_bytes1 = insertelement <4 x b8> <b8 0, b8 poison, b8 poison, b8 poison>, b8 %byte_undef, i32 2 => { b8 0x00 , b8 0x!! , b8 0x19 , b8 0x!! }
+; CHECK-NEXT: %vec_bytes2 = insertelement <4 x b8> %vec_bytes1, b8 %byte_with_provenance, i32 3 => { b8 0x00 , b8 0x!! , b8 0x19 , b8 00001000(00010011) }
+; CHECK-NEXT: %bytes = bitcast <4 x b8> %vec_bytes2 to b32 => b32 0x00 0x!! 0x19 00001000(00010011)
+; CHECK-NEXT: %bytes_freeze = freeze b32 %bytes => b32 0x00 0xF8 0x19 00001000(00010011)
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index d8de9fbe32968..81d995ef69f48 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -269,7 +269,7 @@ define void @main() {
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0xCE [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
-; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 00000000(00100110) 0x!! 0x??
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 00000000(00100110) 0x!! 0x2F
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 0xDE 0xAD 0xBE 0xEF
; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 !!!! 0x!! 0x!! 0x!!
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index 13cd6a415bee8..0d5893cc0a0ce 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -271,7 +271,7 @@ define void @main() {
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %gep_second_byte, align 1
; CHECK-NEXT: %gep_third_byte = getelementptr i8, ptr %alloc_byte, i64 2 => ptr 0xCE [alloc_byte + 2]
; CHECK-NEXT: store b8 poison, ptr %gep_third_byte, align 1
-; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 11000000(00101100) 0x!! 0x??
+; CHECK-NEXT: %bytes_mixed = load b32, ptr %alloc_byte, align 4 => b32 0x7F 11000000(00101100) 0x!! 0x2F
; CHECK-NEXT: store b32 -559038737, ptr %alloc_byte, align 4
; CHECK-NEXT: %bytes_endianness = load b32, ptr %alloc_byte, align 4 => b32 0xEF 0xBE 0xAD 0xDE
; CHECK-NEXT: %bytes_non_pow2 = load b28, ptr %alloc_byte, align 4 => b28 0x!! 0x!! 0x!! !!!!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index a0097878b5062..20a1b9f5b29a7 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -487,8 +487,8 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
if (ContainsUndefinedBits)
*ContainsUndefinedBits = true;
- if (!IsByteType && getEffectiveUndefValueBehavior() ==
- UndefValueBehavior::NonDeterministic) {
+ if (getEffectiveUndefValueBehavior() ==
+ UndefValueBehavior::NonDeterministic) {
// We don't use std::uniform_int_distribution here because it produces
// different results across different library implementations. Instead,
// we directly use the low bits from Rng.
@@ -497,6 +497,8 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
}
if (IsByteType) {
+ LogicalByte.writeBits(~LogicalByte.ConcreteMask & LogicalByte.Value,
+ RandomBits);
LogicalBytes[I / 8] = LogicalByte;
continue;
}
@@ -565,20 +567,31 @@ AnyValue Context::fromBytes(ArrayRef<Byte> Bytes, Type *Ty,
return fromBytes(ConstBytesView(Bytes, DL), Ty, /*OffsetInBits=*/0,
/*CheckPaddingBits=*/true, ContainsUndefinedBits);
if (Ty->isByteTy()) {
- if (ContainsUndefinedBits) {
- for (const Byte &V : Bytes)
- if (V.ConcreteMask != 255) {
- *ContainsUndefinedBits = true;
- break;
- }
- }
unsigned BitWidth = Ty->getByteBitWidth();
if (BitWidth & 7) {
if (!(DL.isLittleEndian() ? Bytes.back() : Bytes.front())
- .AreHighBitsZExtd(BitWidth & 7))
+ .AreHighBitsZExtd(BitWidth & 7)) {
+ if (ContainsUndefinedBits)
+ *ContainsUndefinedBits = true;
return ByteValue::poison(BitWidth, DL.isLittleEndian());
+ }
}
ByteValue Res(BitWidth, Bytes, DL.isLittleEndian());
+ bool HasUndefinedBits = false;
+ for (Byte &V : Res.mutableBytes()) {
+ if (V.ConcreteMask != 255)
+ HasUndefinedBits = true;
+ uint8_t UndefBits = ~V.ConcreteMask & V.Value;
+ if (UndefBits == 0)
+ continue;
+ uint8_t RandomBits = 0;
+ if (getEffectiveUndefValueBehavior() ==
+ UndefValueBehavior::NonDeterministic)
+ RandomBits = static_cast<uint8_t>(Rng());
+ V.writeBits(UndefBits, RandomBits);
+ }
+ if (ContainsUndefinedBits)
+ *ContainsUndefinedBits = HasUndefinedBits;
return AnyValue(std::move(Res));
}
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 80c27077e4e2c..0c02bc80b3af5 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -353,18 +353,21 @@ void ByteValue::print(raw_ostream &OS) const {
if (IsFullByte && V.ConcreteMask == 255 && V.TagMask == 0) {
// Concrete value without provenance.
OS << "0x" << hexdigit(V.Value >> 4) << hexdigit(V.Value & 15);
- } else if (IsFullByte && V.ConcreteMask == 0 &&
- (V.Value == 0 || V.Value == 255)) {
- // Poison/undef bytes.
- OS << (V.Value == 0 ? "0x!!" : "0x??");
+ } else if (IsFullByte && V.ConcreteMask == 0) {
+ assert(V.Value == 0 && "Byte values don't contain undef bits.");
+ // Poison bytes.
+ OS << "0x!!";
} else {
uint32_t BitEnd = IsFullByte ? 8 : BitWidth & 7;
for (uint32_t I = 0; I != BitEnd; ++I) {
uint32_t Mask = 1U << (BitEnd - 1 - I);
if (V.ConcreteMask & Mask)
OS << (V.Value & Mask ? '1' : '0');
- else
- OS << (V.Value & Mask ? '?' : '!');
+ else {
+ assert((V.Value & Mask) == 0 &&
+ "Byte values don't contain undef bits.");
+ OS << '!';
+ }
}
if (uint32_t TagMask = V.ConcreteMask & V.TagMask) {
// Print tags if available.
>From 6ce2f92df96e64e16d402960081b753ad386d085 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 5 Jul 2026 02:19:14 +0800
Subject: [PATCH 16/21] [llubi] Use MaterializedConstant
---
llvm/tools/llubi/lib/Context.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 20a1b9f5b29a7..81a47fd5e8018 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -128,10 +128,13 @@ MaterializedConstant Context::getConstantValueImpl(Constant *C) {
if (auto *CB = dyn_cast<ConstantByte>(C)) {
if (auto *VecTy = dyn_cast<VectorType>(CB->getType()))
- return std::vector<AnyValue>(
- getEVL(VecTy->getElementCount()),
- AnyValue(ByteValue(CB->getValue(), DL.isLittleEndian())));
- return ByteValue(CB->getValue(), DL.isLittleEndian());
+ return MaterializedConstant(
+ std::vector<AnyValue>(
+ getEVL(VecTy->getElementCount()),
+ AnyValue(ByteValue(CB->getValue(), DL.isLittleEndian()))),
+ /*Cacheable=*/true);
+ return MaterializedConstant(ByteValue(CB->getValue(), DL.isLittleEndian()),
+ /*Cacheable=*/true);
}
if (auto *CDS = dyn_cast<ConstantDataSequential>(C)) {
>From 5fb5007b6245d04f153eedb905403db06398f602 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 5 Jul 2026 02:19:26 +0800
Subject: [PATCH 17/21] [llubi] Update OffsetInBits
---
llvm/tools/llubi/lib/Context.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 81a47fd5e8018..e84bed5604c28 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -522,6 +522,8 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
}
}
+ OffsetInBits = NewOffsetInBits;
+
if (IsByteType) {
assert(!CheckPaddingBits &&
"Non-vector-element cases should be handled by the fast path.");
@@ -534,8 +536,6 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
return ByteValue(NumBits, std::move(LogicalBytes), DL.isLittleEndian());
}
- OffsetInBits = NewOffsetInBits;
-
APInt Bits(NumBitsToExtract, RawBits);
// Padding bits for non-byte-sized scalar types must be zero.
>From 2d0045cd9c3da4bd3fddd3edc2dfbe230d499491 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 23 Jul 2026 21:31:05 +0800
Subject: [PATCH 18/21] [llubi] Treat bytes as pointer array
---
llvm/test/tools/llubi/bitcast_be.ll | 2 +-
llvm/test/tools/llubi/bitcast_le.ll | 2 +-
llvm/test/tools/llubi/loadstore_be.ll | 4 ++--
llvm/test/tools/llubi/loadstore_le.ll | 4 ++--
llvm/tools/llubi/lib/Value.cpp | 28 ++++++++++++++++++++++++---
llvm/tools/llubi/lib/Value.h | 2 +-
6 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/llvm/test/tools/llubi/bitcast_be.ll b/llvm/test/tools/llubi/bitcast_be.ll
index 9b962534c3052..d0b3163669c40 100644
--- a/llvm/test/tools/llubi/bitcast_be.ll
+++ b/llvm/test/tools/llubi/bitcast_be.ll
@@ -58,7 +58,7 @@ entry:
; CHECK-NEXT: %bitcast_vec2vec_weird = bitcast <8 x i3> <i3 0, i3 1, i3 2, i3 3, i3 -4, i3 -3, i3 -2, i3 -1> to <3 x i8> => { i8 5, i8 57, i8 119 }
; CHECK-NEXT: %bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half> => { half 0.000000e+00, half 5.960460e-08, half 0.000000e+00, half 1.192090e-07 }
; CHECK-NEXT: %bitcast_floatvec2int = bitcast <4 x half> <half 1.000000e+00, half 2.000000e+00, half 3.000000e+00, half 4.000000e+00> to i64 => i64 4323526012127167488
-; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 00000000(00101000) 00000000(11101000) 00000000(00110111) 00000000(11000101) 00000000(11001011) 00000000(01000001) 00000000(11011100) 00001000(00111110)
+; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 ptr 0x8 [ptr]
; CHECK-NEXT: %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64 => i64 8
; CHECK-NEXT: %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr => ptr 0x8 [ptr]
; CHECK-NEXT: ret void
diff --git a/llvm/test/tools/llubi/bitcast_le.ll b/llvm/test/tools/llubi/bitcast_le.ll
index 7151fc6364020..acb4af6dd73d9 100644
--- a/llvm/test/tools/llubi/bitcast_le.ll
+++ b/llvm/test/tools/llubi/bitcast_le.ll
@@ -58,7 +58,7 @@ entry:
; CHECK-NEXT: %bitcast_vec2vec_weird = bitcast <8 x i3> <i3 0, i3 1, i3 2, i3 3, i3 -4, i3 -3, i3 -2, i3 -1> to <3 x i8> => { i8 -120, i8 -58, i8 -6 }
; CHECK-NEXT: %bitcast_intvec2floatvec = bitcast <2 x i32> <i32 1, i32 2> to <4 x half> => { half 5.960460e-08, half 0.000000e+00, half 1.192090e-07, half 0.000000e+00 }
; CHECK-NEXT: %bitcast_floatvec2int = bitcast <4 x half> <half 1.000000e+00, half 2.000000e+00, half 3.000000e+00, half 4.000000e+00> to i64 => i64 4899988963420290048
-; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 00001000(00111110) 00000000(11011100) 00000000(01000001) 00000000(11001011) 00000000(11000101) 00000000(00110111) 00000000(11101000) 00000000(00101000)
+; CHECK-NEXT: %bitcast_ptr2b64 = bitcast ptr %ptr to b64 => b64 ptr 0x8 [ptr]
; CHECK-NEXT: %bitcast_b642i64 = bitcast b64 %bitcast_ptr2b64 to i64 => i64 8
; CHECK-NEXT: %bitcast_b642ptr = bitcast b64 %bitcast_ptr2b64 to ptr => ptr 0x8 [ptr]
; CHECK-NEXT: ret void
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index 81d995ef69f48..c5c1245c4e722 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -241,7 +241,7 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 11000000(00101100)
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
@@ -261,7 +261,7 @@ define void @main() {
; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 00000000(00100110) 00000000(01110001) 00000000(00111100) 00000000(00111001) 00000000(10001010) 00000000(10010101) 00000000(00001000) 11000000(00101100)
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index 0d5893cc0a0ce..ff4e5f6ceff33 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -243,7 +243,7 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 11000000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -64
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
@@ -263,7 +263,7 @@ define void @main() {
; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 1(0) , b1 1(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(1) , b1 1(0) , b1 1(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 11000000(00101100) 00000000(00001000) 00000000(10010101) 00000000(10001010) 00000000(00111001) 00000000(00111100) 00000000(01110001) 00000000(00100110)
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 0c02bc80b3af5..6f30c2067245b 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -119,7 +119,7 @@ void AnyValue::print(Context &Ctx, raw_ostream &OS) const {
PtrVal.print(OS);
break;
case StorageKind::Byte:
- ByteVal.print(OS);
+ ByteVal.print(Ctx, OS);
break;
case StorageKind::Poison:
OS << "poison";
@@ -344,9 +344,10 @@ ByteValue ByteValue::poison(uint32_t BitWidth, bool IsLittleEndian) {
IsLittleEndian, /*ImplicitClearHighBits=*/true);
}
-void ByteValue::print(raw_ostream &OS) const {
+void ByteValue::print(Context &Ctx, raw_ostream &OS) const {
OS << 'b' << BitWidth << ' ';
- for (const Byte &V : Val) {
+
+ auto PrintByte = [&](const Byte &V) {
bool IsFullByte = (BitWidth & 7) == 0 ||
(IsLittleEndian ? &Val.back() : &Val.front()) != &V;
// Try to print a byte in short form
@@ -383,6 +384,27 @@ void ByteValue::print(raw_ostream &OS) const {
}
}
OS << ' ';
+ };
+
+ auto &DL = Ctx.getDataLayout();
+ unsigned PtrWidthForAS0 = DL.getPointerSizeInBits(0);
+ Type *PtrTy = PointerType::getUnqual(Ctx.getContext());
+
+ if (PtrWidthForAS0 % 8 == 0 && BitWidth % PtrWidthForAS0 == 0) {
+ // Try to treat the bytes value as an array of pointers in address space 0.
+ unsigned PtrSize = PtrWidthForAS0 / 8;
+ for (size_t I = 0, E = Val.size(); I != E; I += PtrSize) {
+ auto Res = Ctx.fromBytes(ArrayRef(Val).slice(I, PtrSize), PtrTy);
+ if (Res.isPointer()) {
+ Res.asPointer().print(OS);
+ } else {
+ for (size_t J = 0; J != PtrSize; ++J)
+ PrintByte(Val[I + J]);
+ }
+ }
+ } else {
+ for (const Byte &V : Val)
+ PrintByte(V);
}
}
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 9e30c2cda1fc3..982871427562b 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -284,7 +284,7 @@ class ByteValue {
uint32_t getBitWidth() const { return BitWidth; }
ArrayRef<Byte> bytes() const { return Val; }
MutableArrayRef<Byte> mutableBytes() { return Val; }
- void print(raw_ostream &OS) const;
+ void print(Context &Ctx, raw_ostream &OS) const;
};
// Value representation for actual values of LLVM values.
>From db317fd4cd898e50981f3ebc908619f738ee8366 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 23 Jul 2026 21:37:13 +0800
Subject: [PATCH 19/21] [llubi] Add counterexample
---
llvm/test/tools/llubi/loadstore_be.ll | 19 +++++++++++++++++++
llvm/test/tools/llubi/loadstore_le.ll | 19 +++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index c5c1245c4e722..b651e39ba5eb1 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -157,6 +157,16 @@ define void @main() {
store b8 255, ptr %alloc_byte
%bytes_zextd_poison = load b7, ptr %alloc_byte
+ %alloc_ptr_array = alloca [4 x ptr]
+ store i64 0, ptr %alloc_ptr_array
+ %ptr_array_1 = getelementptr ptr, ptr %alloc_ptr_array, i64 1
+ store ptr %alloc_ptr_array, ptr %ptr_array_1
+ %ptr_array_2 = getelementptr ptr, ptr %alloc_ptr_array, i64 2
+ store ptr %alloc_ptr_array, ptr %ptr_array_2
+ %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20
+ store i8 0, ptr %ptr_array_2_middle
+ %mixed_alloc = load b256, ptr %alloc_ptr_array
+
ret void
}
; CHECK: Entering function: main
@@ -277,5 +287,14 @@ define void @main() {
; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0x0E 0xAD 0xBE 0xEF
; CHECK-NEXT: store b8 -1, ptr %alloc_byte, align 1
; CHECK-NEXT: %bytes_zextd_poison = load b7, ptr %alloc_byte, align 1 => b7 !!!!!!!
+; CHECK-NEXT: %alloc_ptr_array = alloca [4 x ptr], align 8 => ptr 0xD8 [alloc_ptr_array]
+; CHECK-NEXT: store i64 0, ptr %alloc_ptr_array, align 8
+; CHECK-NEXT: %ptr_array_1 = getelementptr ptr, ptr %alloc_ptr_array, i64 1 => ptr 0xE0 [alloc_ptr_array + 8]
+; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_1, align 8
+; CHECK-NEXT: %ptr_array_2 = getelementptr ptr, ptr %alloc_ptr_array, i64 2 => ptr 0xE8 [alloc_ptr_array + 16]
+; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_2, align 8
+; CHECK-NEXT: %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20 => ptr 0xEC [alloc_ptr_array + 20]
+; CHECK-NEXT: store i8 0, ptr %ptr_array_2_middle, align 1
+; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 ptr 0x0 [nullary]ptr 0xD8 [alloc_ptr_array]ptr 0xD8 [nullary]ptr 0x2ADF69B8978BB307 [nullary]
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index ff4e5f6ceff33..e50cb71d629d2 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -158,6 +158,16 @@ define void @main() {
store b8 255, ptr %alloc_byte
%bytes_zextd_poison = load b7, ptr %alloc_byte
+ %alloc_ptr_array = alloca [4 x ptr]
+ store i64 0, ptr %alloc_ptr_array
+ %ptr_array_1 = getelementptr ptr, ptr %alloc_ptr_array, i64 1
+ store ptr %alloc_ptr_array, ptr %ptr_array_1
+ %ptr_array_2 = getelementptr ptr, ptr %alloc_ptr_array, i64 2
+ store ptr %alloc_ptr_array, ptr %ptr_array_2
+ %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20
+ store i8 0, ptr %ptr_array_2_middle
+ %mixed_alloc = load b256, ptr %alloc_ptr_array
+
ret void
}
; CHECK: Entering function: main
@@ -279,5 +289,14 @@ define void @main() {
; CHECK-NEXT: %bytes_zextd = load b32, ptr %alloc_byte, align 4 => b32 0xEF 0xBE 0xAD 0x0E
; CHECK-NEXT: store b8 -1, ptr %alloc_byte, align 1
; CHECK-NEXT: %bytes_zextd_poison = load b7, ptr %alloc_byte, align 1 => b7 !!!!!!!
+; CHECK-NEXT: %alloc_ptr_array = alloca [4 x ptr], align 8 => ptr 0xD8 [alloc_ptr_array]
+; CHECK-NEXT: store i64 0, ptr %alloc_ptr_array, align 8
+; CHECK-NEXT: %ptr_array_1 = getelementptr ptr, ptr %alloc_ptr_array, i64 1 => ptr 0xE0 [alloc_ptr_array + 8]
+; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_1, align 8
+; CHECK-NEXT: %ptr_array_2 = getelementptr ptr, ptr %alloc_ptr_array, i64 2 => ptr 0xE8 [alloc_ptr_array + 16]
+; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_2, align 8
+; CHECK-NEXT: %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20 => ptr 0xEC [alloc_ptr_array + 20]
+; CHECK-NEXT: store i8 0, ptr %ptr_array_2_middle, align 1
+; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 ptr 0x0 [nullary]ptr 0xD8 [alloc_ptr_array]ptr 0xD8 [nullary]ptr 0x7B38B97B869DF2A [nullary]
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
>From 4fdc402b62692cd4ddf3290e9e51c04bbbe4d5c3 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 23 Jul 2026 21:48:30 +0800
Subject: [PATCH 20/21] [llubi] Make sure the byte array is full pointer
---
llvm/test/tools/llubi/loadstore_be.ll | 6 +++---
llvm/test/tools/llubi/loadstore_le.ll | 6 +++---
llvm/tools/llubi/lib/Value.cpp | 20 ++++++++++++++------
3 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/llvm/test/tools/llubi/loadstore_be.ll b/llvm/test/tools/llubi/loadstore_be.ll
index b651e39ba5eb1..569dc12762f54 100644
--- a/llvm/test/tools/llubi/loadstore_be.ll
+++ b/llvm/test/tools/llubi/loadstore_be.ll
@@ -251,7 +251,7 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 0
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
@@ -271,7 +271,7 @@ define void @main() {
; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
@@ -295,6 +295,6 @@ define void @main() {
; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_2, align 8
; CHECK-NEXT: %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20 => ptr 0xEC [alloc_ptr_array + 20]
; CHECK-NEXT: store i8 0, ptr %ptr_array_2_middle, align 1
-; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 ptr 0x0 [nullary]ptr 0xD8 [alloc_ptr_array]ptr 0xD8 [nullary]ptr 0x2ADF69B8978BB307 [nullary]
+; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ptr 0xD8 [alloc_ptr_array] 00000000(10001101) 00000000(11101101) 00000000(10101000) 00000000(01010011) 0x00 00000000(10100110) 00000000(00010110) 11011000(00011010) 0x2A 0xDF 0x69 0xB8 0x97 0x8B 0xB3 0x07
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/loadstore_le.ll b/llvm/test/tools/llubi/loadstore_le.ll
index e50cb71d629d2..a3acec723bf5f 100644
--- a/llvm/test/tools/llubi/loadstore_le.ll
+++ b/llvm/test/tools/llubi/loadstore_le.ll
@@ -253,7 +253,7 @@ define void @main() {
; CHECK-NEXT: %load_struct_noundef = load { i8, i32 }, ptr %alloc_struct_padding, align 4, !noundef !0 => { i8 0, i32 0 }
; CHECK-NEXT: %alloc_ptr = alloca ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: store ptr %alloc_ptr, ptr %alloc_ptr, align 8
-; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bytes = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %addr_bits = load i8, ptr %alloc_ptr, align 1 => i8 -64
; CHECK-NEXT: store i8 %addr_bits, ptr %alloc_ptr, align 1
@@ -273,7 +273,7 @@ define void @main() {
; CHECK-NEXT: %first_byte_with_provenance_reversed_v8b1 = bitcast b8 %first_byte_with_provenance_reversed to <8 x b1> => { b1 1(0) , b1 1(0) , b1 0(1) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(0) }
; CHECK-NEXT: %first_byte_with_provenance_v8b1 = call <8 x b1> @llvm.vector.reverse.v8b1(<8 x b1> %first_byte_with_provenance_reversed_v8b1) => { b1 0(0) , b1 0(0) , b1 0(1) , b1 0(1) , b1 0(0) , b1 0(1) , b1 1(0) , b1 1(0) }
; CHECK-NEXT: store <8 x b1> %first_byte_with_provenance_v8b1, ptr %alloc_ptr, align 1
-; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
+; CHECK-NEXT: %bytes_recovered = load b64, ptr %alloc_ptr, align 8 => b64 ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %ptr_with_provenance3 = load ptr, ptr %alloc_ptr, align 8 => ptr 0xC0 [alloc_ptr]
; CHECK-NEXT: %alloc_byte = alloca b32, align 4 => ptr 0xCC [alloc_byte]
; CHECK-NEXT: store b8 127, ptr %alloc_byte, align 1
@@ -297,6 +297,6 @@ define void @main() {
; CHECK-NEXT: store ptr %alloc_ptr_array, ptr %ptr_array_2, align 8
; CHECK-NEXT: %ptr_array_2_middle = getelementptr i8, ptr %alloc_ptr_array, i64 20 => ptr 0xEC [alloc_ptr_array + 20]
; CHECK-NEXT: store i8 0, ptr %ptr_array_2_middle, align 1
-; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 ptr 0x0 [nullary]ptr 0xD8 [alloc_ptr_array]ptr 0xD8 [nullary]ptr 0x7B38B97B869DF2A [nullary]
+; CHECK-NEXT: %mixed_alloc = load b256, ptr %alloc_ptr_array, align 8 => b256 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ptr 0xD8 [alloc_ptr_array] 11011000(00011010) 00000000(00010110) 00000000(10100110) 00000000(01111111) 0x00 00000000(10101000) 00000000(11101101) 00000000(10001101) 0x2A 0xDF 0x69 0xB8 0x97 0x8B 0xB3 0x07
; CHECK-NEXT: ret void
; CHECK-NEXT: Exiting function: main
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 6f30c2067245b..273f0c3648917 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -394,13 +394,21 @@ void ByteValue::print(Context &Ctx, raw_ostream &OS) const {
// Try to treat the bytes value as an array of pointers in address space 0.
unsigned PtrSize = PtrWidthForAS0 / 8;
for (size_t I = 0, E = Val.size(); I != E; I += PtrSize) {
- auto Res = Ctx.fromBytes(ArrayRef(Val).slice(I, PtrSize), PtrTy);
- if (Res.isPointer()) {
- Res.asPointer().print(OS);
- } else {
- for (size_t J = 0; J != PtrSize; ++J)
- PrintByte(Val[I + J]);
+ ArrayRef<Byte> Slice = ArrayRef(Val).slice(I, PtrSize);
+ if (all_of(Slice, [](const Byte &V) {
+ return (V.ConcreteMask & V.TagMask) == 255;
+ })) {
+ AnyValue Res = Ctx.fromBytes(Slice, PtrTy);
+ if (Res.isPointer()) {
+ Res.asPointer().print(OS);
+ OS << ' ';
+ continue;
+ }
}
+
+ // Otherwise, fallback into bytes array
+ for (size_t J = 0; J != PtrSize; ++J)
+ PrintByte(Val[I + J]);
}
} else {
for (const Byte &V : Val)
>From 8495aa82f24afb4c38069ccca3c9d04a42f31f87 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 23 Jul 2026 23:46:57 +0800
Subject: [PATCH 21/21] [llubi] Address review comments.
---
llvm/tools/llubi/lib/Context.cpp | 7 ++++---
llvm/tools/llubi/lib/Value.cpp | 8 +++++---
llvm/tools/llubi/lib/Value.h | 2 ++
3 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index e84bed5604c28..c9a40392611ae 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -452,6 +452,7 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
constexpr uint32_t WordBits = APInt::APINT_BITS_PER_WORD;
SmallVector<APInt::WordType> RawBits;
bool IsByteType = Ty->isByteTy();
+ // LogicalBytes is always little-endian.
std::vector<Byte> LogicalBytes;
if (IsByteType)
LogicalBytes.resize(divideCeil(NumBitsToExtract, 8));
@@ -512,7 +513,9 @@ AnyValue Context::fromBytes(ConstBytesView Bytes, Type *Ty,
RawBits[I / WordBits] |= static_cast<APInt::WordType>(ActualBits)
<< (I % WordBits);
if (IsTagValid) {
- if ((LogicalByte.TagMask & LogicalByte.ConcreteMask & Mask) == Mask) {
+ assert((LogicalByte.TagMask & LogicalByte.ConcreteMask) ==
+ LogicalByte.TagMask);
+ if ((LogicalByte.TagMask & Mask) == Mask) {
uint8_t ActualTagBits = LogicalByte.TagValue & Mask;
RawTagBits[I / WordBits] |= static_cast<APInt::WordType>(ActualTagBits)
<< (I % WordBits);
@@ -884,8 +887,6 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
V.Value &= OldMask;
if (mayUseNonDeterminism())
V.Value |= (Rng() & 255) & ~OldMask;
- V.TagMask &= OldMask;
- V.TagValue &= OldMask;
}
return;
}
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 273f0c3648917..2089977973f5a 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -370,12 +370,13 @@ void ByteValue::print(Context &Ctx, raw_ostream &OS) const {
OS << '!';
}
}
- if (uint32_t TagMask = V.ConcreteMask & V.TagMask) {
+ assert((V.ConcreteMask & V.TagMask) == V.TagMask);
+ if (V.TagMask) {
// Print tags if available.
OS << '(';
for (uint32_t I = 0; I != BitEnd; ++I) {
uint32_t Mask = 1U << (BitEnd - 1 - I);
- if (TagMask & Mask)
+ if (V.TagMask & Mask)
OS << (V.TagValue & Mask ? '1' : '0');
else
OS << '!';
@@ -396,7 +397,8 @@ void ByteValue::print(Context &Ctx, raw_ostream &OS) const {
for (size_t I = 0, E = Val.size(); I != E; I += PtrSize) {
ArrayRef<Byte> Slice = ArrayRef(Val).slice(I, PtrSize);
if (all_of(Slice, [](const Byte &V) {
- return (V.ConcreteMask & V.TagMask) == 255;
+ assert((V.ConcreteMask & V.TagMask) == V.TagMask);
+ return V.TagMask == 255;
})) {
AnyValue Res = Ctx.fromBytes(Slice, PtrTy);
if (Res.isPointer()) {
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 982871427562b..24fd81aa38629 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -59,11 +59,13 @@ struct Byte {
void poisonBits(uint8_t Mask) {
ConcreteMask &= ~Mask;
Value &= ~Mask;
+ TagMask &= ~Mask;
}
void undefBits(uint8_t Mask) {
ConcreteMask &= ~Mask;
Value |= Mask;
+ TagMask &= ~Mask;
}
void writeBits(uint8_t Mask, uint8_t Val) {
More information about the llvm-commits
mailing list