[llvm] [llubi] Add support for undef values (PR #205602)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 27 09:21:46 PDT 2026
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/205602
>From ee82f5c9ad547b7b3970b8a471a63d3fd3bd6daf Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 25 Jun 2026 00:49:40 +0800
Subject: [PATCH 1/5] [llubi] Add pre-commit tests. NFC.
---
llvm/test/tools/llubi/undef.ll | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 llvm/test/tools/llubi/undef.ll
diff --git a/llvm/test/tools/llubi/undef.ll b/llvm/test/tools/llubi/undef.ll
new file mode 100644
index 0000000000000..efe07d70d6a9a
--- /dev/null
+++ b/llvm/test/tools/llubi/undef.ll
@@ -0,0 +1,11 @@
+; 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
+
+ at g = global i32 undef
+
+define void @main() {
+ %add = add i32 1, undef
+ %load = load i32, ptr @g
+ ret void
+}
+; CHECK: error: Failed to initialize global values (e.g., the memory limit may be too low).
>From a24e2bad7f892be9dec9c929e0a0f3a0f01eaceb Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Thu, 25 Jun 2026 00:58:27 +0800
Subject: [PATCH 2/5] [llubi] Use null values to represent undef
---
llvm/test/tools/llubi/undef.ll | 8 ++++++--
llvm/test/tools/llubi/unsupported_constant.ll | 12 ------------
llvm/tools/llubi/lib/Context.cpp | 3 +++
3 files changed, 9 insertions(+), 14 deletions(-)
delete mode 100644 llvm/test/tools/llubi/unsupported_constant.ll
diff --git a/llvm/test/tools/llubi/undef.ll b/llvm/test/tools/llubi/undef.ll
index efe07d70d6a9a..d6f337c2887e6 100644
--- a/llvm/test/tools/llubi/undef.ll
+++ b/llvm/test/tools/llubi/undef.ll
@@ -1,5 +1,5 @@
; 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: llubi --verbose < %s 2>&1 | FileCheck %s
@g = global i32 undef
@@ -8,4 +8,8 @@ define void @main() {
%load = load i32, ptr @g
ret void
}
-; CHECK: error: Failed to initialize global values (e.g., the memory limit may be too low).
+; CHECK: Entering function: main
+; CHECK-NEXT: %add = add i32 1, undef => i32 1
+; CHECK-NEXT: %load = load i32, ptr @g, align 4 => i32 0
+; CHECK-NEXT: ret void
+; CHECK-NEXT: Exiting function: main
diff --git a/llvm/test/tools/llubi/unsupported_constant.ll b/llvm/test/tools/llubi/unsupported_constant.ll
deleted file mode 100644
index 96d23cff4904a..0000000000000
--- a/llvm/test/tools/llubi/unsupported_constant.ll
+++ /dev/null
@@ -1,12 +0,0 @@
-; 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
-
-define void @main() {
- %res = add i32 0, undef
- ret void
-}
-; CHECK: Entering function: main
-; CHECK-NEXT: Stacktrace:
-; CHECK-NEXT: #0 %res = add i32 0, undef at @main <stdin>:5
-; CHECK-NEXT: Error: Unsupported constant: i32 undef.
-; CHECK-NEXT: error: Execution of function 'main' failed.
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 5c13ff8a47c24..750c8ce43ab11 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -91,6 +91,9 @@ std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
if (isa<PoisonValue>(C))
return AnyValue::getPoisonValue(*this, C->getType());
+ if (isa<UndefValue>(C))
+ return AnyValue::getNullValue(*this, C->getType());
+
if (isa<ConstantAggregateZero>(C))
return AnyValue::getNullValue(*this, C->getType());
>From 05a49d1080ca3c9f368a62953b5256acb8eb4dc9 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 27 Jun 2026 23:34:32 +0800
Subject: [PATCH 3/5] [llubi] Use random value.
---
llvm/test/tools/llubi/undef.ll | 28 ++++-
llvm/tools/llubi/lib/Context.cpp | 150 +++++++++++++++++----------
llvm/tools/llubi/lib/Context.h | 27 ++++-
llvm/tools/llubi/lib/Interpreter.cpp | 1 +
4 files changed, 144 insertions(+), 62 deletions(-)
diff --git a/llvm/test/tools/llubi/undef.ll b/llvm/test/tools/llubi/undef.ll
index d6f337c2887e6..4e28ce89f1738 100644
--- a/llvm/test/tools/llubi/undef.ll
+++ b/llvm/test/tools/llubi/undef.ll
@@ -4,12 +4,32 @@
@g = global i32 undef
define void @main() {
- %add = add i32 1, undef
- %load = load i32, ptr @g
+ %add1 = add i32 1, undef
+ %add2 = add i32 1, undef
+ %load1 = load i32, ptr @g
+ %load2 = load i32, ptr @g
+ %fadd1 = fadd float 0.0, undef
+ %fadd2 = fadd float 0.0, undef
+ %vec1 = add <4 x i32> zeroinitializer, undef
+ %vec2 = add <4 x i32> zeroinitializer, undef
+ %agg1 = select i1 false, [4 x {i32, <2 x i8>}] zeroinitializer, [4 x {i32, <2 x i8>}] undef
+ %agg2 = select i1 false, [4 x {i32, <2 x i8>}] zeroinitializer, [4 x {i32, <2 x i8>}] undef
+ %ptr1 = getelementptr i8, ptr undef, i64 0
+ %ptr2 = getelementptr i8, ptr undef, i64 0
ret void
}
; CHECK: Entering function: main
-; CHECK-NEXT: %add = add i32 1, undef => i32 1
-; CHECK-NEXT: %load = load i32, ptr @g, align 4 => i32 0
+; CHECK-NEXT: %add1 = add i32 1, undef => i32 1044445580
+; CHECK-NEXT: %add2 = add i32 1, undef => i32 -265099078
+; CHECK-NEXT: %load1 = load i32, ptr @g, align 4 => i32 -884876226
+; CHECK-NEXT: %load2 = load i32, ptr @g, align 4 => i32 -884876226
+; CHECK-NEXT: %fadd1 = fadd float 0.000000e+00, undef => float 0xCEE3FCEE
+; CHECK-NEXT: %fadd2 = fadd float 0.000000e+00, undef => float 0x3B0725AC
+; CHECK-NEXT: %vec1 = add <4 x i32> zeroinitializer, undef => { i32 -44045842, i32 69919023, i32 1166165736, i32 1868579686 }
+; CHECK-NEXT: %vec2 = add <4 x i32> zeroinitializer, undef => { i32 635390489, i32 84751635, i32 -1973618952, i32 491226766 }
+; CHECK-NEXT: %agg1 = select i1 false, [4 x { i32, <2 x i8> }] zeroinitializer, [4 x { i32, <2 x i8> }] undef => { { i32 374338310, { i8 56, i8 65 } }, { i32 -726234893, { i8 73, i8 117 } }, { i32 1518296732, { i8 15, i8 -15 } }, { i32 1577165154, { i8 -54, i8 44 } } }
+; CHECK-NEXT: %agg2 = select i1 false, [4 x { i32, <2 x i8> }] zeroinitializer, [4 x { i32, <2 x i8> }] undef => { { i32 -1762085585, { i8 26, i8 42 } }, { i32 -1961199649, { i8 105, i8 -72 } }, { i32 -1877308265, { i8 -117, i8 -77 } }, { i32 1291699207, { i8 79, i8 -65 } } }
+; CHECK-NEXT: %ptr1 = getelementptr i8, ptr undef, i64 0 => ptr 0xAB8966E4AA3D91A2 [nullary]
+; CHECK-NEXT: %ptr2 = getelementptr i8, ptr undef, i64 0 => ptr 0xC48CA8F37ED75121 [nullary]
; 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 750c8ce43ab11..b9315e70a4c0f 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -87,65 +87,81 @@ bool Context::initGlobalValues() {
return true;
}
-std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
+MaterializedConstant Context::getConstantValueImpl(Constant *C) {
if (isa<PoisonValue>(C))
- return AnyValue::getPoisonValue(*this, C->getType());
-
- if (isa<UndefValue>(C))
- return AnyValue::getNullValue(*this, C->getType());
+ return MaterializedConstant(AnyValue::getPoisonValue(*this, C->getType()),
+ /*Cacheable=*/true);
+
+ if (isa<UndefValue>(C)) {
+ // We treat undef as a freshly freeze poison.
+ auto Value = AnyValue::getPoisonValue(*this, C->getType());
+ freeze(Value, C->getType());
+ return MaterializedConstant(std::move(Value), /*Cacheable=*/false);
+ }
if (isa<ConstantAggregateZero>(C))
- return AnyValue::getNullValue(*this, C->getType());
+ return MaterializedConstant(AnyValue::getNullValue(*this, C->getType()),
+ /*Cacheable=*/true);
if (isa<ConstantPointerNull>(C))
- return AnyValue::getNullValue(*this, C->getType());
+ return MaterializedConstant(AnyValue::getNullValue(*this, C->getType()),
+ /*Cacheable=*/true);
if (auto *CI = dyn_cast<ConstantInt>(C)) {
if (auto *VecTy = dyn_cast<VectorType>(CI->getType()))
- return std::vector<AnyValue>(getEVL(VecTy->getElementCount()),
- AnyValue(CI->getValue()));
- return CI->getValue();
+ return MaterializedConstant(
+ std::vector<AnyValue>(getEVL(VecTy->getElementCount()),
+ AnyValue(CI->getValue())),
+ /*Cacheable=*/true);
+ return MaterializedConstant(CI->getValue(), /*Cacheable=*/true);
}
if (auto *CFP = dyn_cast<ConstantFP>(C)) {
if (auto *VecTy = dyn_cast<VectorType>(CFP->getType()))
- return std::vector<AnyValue>(getEVL(VecTy->getElementCount()),
- AnyValue(CFP->getValue()));
- return CFP->getValue();
+ return MaterializedConstant(
+ std::vector<AnyValue>(getEVL(VecTy->getElementCount()),
+ AnyValue(CFP->getValue())),
+ /*Cacheable=*/true);
+ return MaterializedConstant(CFP->getValue(), /*Cacheable=*/true);
}
if (auto *CDS = dyn_cast<ConstantDataSequential>(C)) {
std::vector<AnyValue> Elts;
Elts.reserve(CDS->getNumElements());
+ bool Cacheable = true;
for (uint32_t I = 0, E = CDS->getNumElements(); I != E; ++I) {
- const AnyValue *Elt = getConstantValue(CDS->getElementAsConstant(I));
+ auto Elt = getConstantValue(CDS->getElementAsConstant(I));
if (!Elt)
return std::nullopt;
+ Cacheable &= Elt->isCacheable();
Elts.push_back(*Elt);
}
- return std::move(Elts);
+ return MaterializedConstant(std::move(Elts), Cacheable);
}
if (auto *CA = dyn_cast<ConstantAggregate>(C)) {
std::vector<AnyValue> Elts;
Elts.reserve(CA->getNumOperands());
+ bool Cacheable = true;
for (uint32_t I = 0, E = CA->getNumOperands(); I != E; ++I) {
- const AnyValue *Elt = getConstantValue(CA->getOperand(I));
+ auto Elt = getConstantValue(CA->getOperand(I));
if (!Elt)
return std::nullopt;
+ Cacheable &= Elt->isCacheable();
Elts.push_back(*Elt);
}
- return std::move(Elts);
+ return MaterializedConstant(std::move(Elts), Cacheable);
}
if (auto *BA = dyn_cast<BlockAddress>(C))
- return BlockAddrMap.at(BA->getBasicBlock());
+ return MaterializedConstant(BlockAddrMap.at(BA->getBasicBlock()),
+ /*Cacheable=*/true);
if (auto *GV = dyn_cast<GlobalVariable>(C))
- return GlobalAddrMap.at(GV);
+ return MaterializedConstant(GlobalAddrMap.at(GV), /*Cacheable=*/true);
if (auto *F = dyn_cast<Function>(C))
- return FuncAddrMap.at(F);
+ return MaterializedConstant(FuncAddrMap.at(F), /*Cacheable=*/true);
if (auto *CE = dyn_cast<ConstantExpr>(C))
return evaluateConstantExpression(CE);
@@ -153,69 +169,77 @@ std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
return std::nullopt;
}
-std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
+MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
unsigned Opc = CE->getOpcode();
switch (Opc) {
case Instruction::Trunc: {
- const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ const MaterializedConstant *Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
if (Src->isPoison())
- return AnyValue::poison();
+ return MaterializedConstant(AnyValue::poison(), Src->isCacheable());
unsigned BitWidth = CE->getType()->getScalarSizeInBits();
if (Src->isInteger())
- return AnyValue(Src->asInteger().trunc(BitWidth));
+ return MaterializedConstant(Src->asInteger().trunc(BitWidth),
+ Src->isCacheable());
std::vector<AnyValue> Vec = Src->asAggregate();
for (auto &V : Vec) {
if (V.isInteger())
V = V.asInteger().trunc(BitWidth);
}
- return AnyValue(std::move(Vec));
+ return MaterializedConstant(std::move(Vec), Src->isCacheable());
}
case Instruction::BitCast: {
Constant *SrcOp = CE->getOperand(0);
- const AnyValue *Src = getConstantValue(SrcOp);
+ auto Src = getConstantValue(SrcOp);
if (!Src)
return std::nullopt;
SmallVector<Byte> Bytes;
Bytes.resize(getEffectiveTypeStoreSize(CE->getType()), Byte::concrete(0));
toBytes(*Src, SrcOp->getType(), Bytes);
- return fromBytes(Bytes, CE->getType());
+ return MaterializedConstant(fromBytes(Bytes, CE->getType()),
+ Src->isCacheable());
}
case Instruction::InsertElement: {
- const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ auto Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
- const AnyValue *Val = getConstantValue(CE->getOperand(1));
+ auto Val = getConstantValue(CE->getOperand(1));
if (!Val)
return std::nullopt;
- const AnyValue *Idx = getConstantValue(CE->getOperand(2));
+ auto Idx = getConstantValue(CE->getOperand(2));
if (!Idx)
return std::nullopt;
auto &SrcVec = Src->asAggregate();
+ bool Cacheable =
+ Src->isCacheable() && Val->isCacheable() && Idx->isCacheable();
if (Idx->isPoison() || Idx->asInteger().uge(SrcVec.size()))
- return AnyValue::getPoisonValue(*this, CE->getType());
+ return MaterializedConstant(
+ AnyValue::getPoisonValue(*this, CE->getType()), Cacheable);
std::vector<AnyValue> ResVec = SrcVec;
ResVec[Idx->asInteger().getZExtValue()] = *Val;
- return AnyValue(std::move(ResVec));
+ return MaterializedConstant(std::move(ResVec), Cacheable);
}
case Instruction::ExtractElement: {
- const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ auto Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
- const AnyValue *Idx = getConstantValue(CE->getOperand(1));
+ auto Idx = getConstantValue(CE->getOperand(1));
if (!Idx)
return std::nullopt;
auto &SrcVec = Src->asAggregate();
+ bool Cacheable = Src->isCacheable() && Idx->isCacheable();
if (Idx->isPoison() || Idx->asInteger().uge(SrcVec.size()))
- return AnyValue::getPoisonValue(*this, CE->getType());
- return SrcVec[Idx->asInteger().getZExtValue()];
+ return MaterializedConstant(
+ AnyValue::getPoisonValue(*this, CE->getType()), Cacheable);
+ return MaterializedConstant(SrcVec[Idx->asInteger().getZExtValue()],
+ Cacheable);
}
case Instruction::ShuffleVector: {
- const AnyValue *LHS = getConstantValue(CE->getOperand(0));
+ auto LHS = getConstantValue(CE->getOperand(0));
if (!LHS)
return std::nullopt;
- const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+ auto RHS = getConstantValue(CE->getOperand(1));
if (!RHS)
return std::nullopt;
auto &LHSVec = LHS->asAggregate();
@@ -240,40 +264,45 @@ std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
Res.push_back(RHSVec[Idx - Size]);
}
}
- return AnyValue(std::move(Res));
+ return MaterializedConstant(std::move(Res),
+ LHS->isCacheable() && RHS->isCacheable());
}
case Instruction::GetElementPtr: {
// Temporary variable for reference to poison values when the subexpression
// cannot be evaluated. As the reference will be consumed immediately, we
// don't need to store them into a list.
AnyValue PoisonValue;
+ bool Cacheable = true;
AnyValue Res =
computeGEP(*cast<GEPOperator>(CE), [&](Value *V) -> const AnyValue & {
- const AnyValue *Val = getConstantValue(cast<Constant>(V));
- if (Val)
+ auto Val = getConstantValue(cast<Constant>(V));
+ if (Val) {
+ Cacheable &= Val->isCacheable();
return *Val;
+ }
PoisonValue = AnyValue::getPoisonValue(*this, V->getType());
return PoisonValue;
});
if (!PoisonValue.isNone())
return std::nullopt;
- return std::move(Res);
+ return MaterializedConstant(std::move(Res), Cacheable);
}
case Instruction::PtrToAddr: {
- const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ auto Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
if (Src->isPoison())
- return AnyValue::poison();
+ return MaterializedConstant(AnyValue::poison(), Src->isCacheable());
unsigned BitWidth = CE->getType()->getScalarSizeInBits();
if (Src->isPointer())
- return Src->asPointer().address().trunc(BitWidth);
+ return MaterializedConstant(Src->asPointer().address().trunc(BitWidth),
+ Src->isCacheable());
std::vector<AnyValue> Vec = Src->asAggregate();
for (auto &V : Vec) {
if (V.isPointer())
V = V.asPointer().address().trunc(BitWidth);
}
- return AnyValue(std::move(Vec));
+ return MaterializedConstant(std::move(Vec), Src->isCacheable());
}
case Instruction::PtrToInt:
case Instruction::IntToPtr:
@@ -281,10 +310,10 @@ std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
return std::nullopt;
default:
assert(Instruction::isBinaryOp(Opc) && "Must be binary operator?");
- const AnyValue *LHS = getConstantValue(CE->getOperand(0));
+ auto *LHS = getConstantValue(CE->getOperand(0));
if (!LHS)
return std::nullopt;
- const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+ auto *RHS = getConstantValue(CE->getOperand(1));
if (!RHS)
return std::nullopt;
@@ -313,6 +342,8 @@ std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
}
};
+ bool Cacheable = LHS->isCacheable() && RHS->isCacheable();
+
if (CE->getType()->isVectorTy()) {
auto &LHSVec = LHS->asAggregate();
auto &RHSVec = RHS->asAggregate();
@@ -320,23 +351,34 @@ std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
ResVec.reserve(LHSVec.size());
for (const auto &[ScalarLHS, ScalarRHS] : zip(LHSVec, RHSVec))
ResVec.push_back(ScalarEval(ScalarLHS, ScalarRHS));
- return std::move(ResVec);
+ return MaterializedConstant(std::move(ResVec), Cacheable);
}
- return ScalarEval(*LHS, *RHS);
+ return MaterializedConstant(ScalarEval(*LHS, *RHS), Cacheable);
}
}
-const AnyValue *Context::getConstantValue(Constant *C) {
+const MaterializedConstant *Context::getConstantValue(Constant *C) {
auto It = ConstCache.find(C);
if (It != ConstCache.end())
return &It->second;
- std::optional<AnyValue> Val = getConstantValueImpl(C);
- if (!Val)
+ MaterializedConstant Val = getConstantValueImpl(C);
+ if (Val.isNone())
return nullptr;
+ if (!Val.isCacheable()) {
+ assert(NoncacheableConstBuffer.getBytesAllocated() <=
+ 1024 * sizeof(MaterializedConstant) &&
+ "");
+ return new (NoncacheableConstBuffer.Allocate())
+ MaterializedConstant(std::move(Val));
+ }
+
+ return &ConstCache.emplace(C, std::move(Val)).first->second;
+}
- return &ConstCache.emplace(C, std::move(*Val)).first->second;
+void Context::resetNoncacheableConstantBuffer() {
+ NoncacheableConstBuffer.DestroyAll();
}
APInt Context::getTag(uint32_t BitWidth, Provenance &Prov) {
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 5f8b79a6b183c..491876d8ddbe4 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -201,6 +201,17 @@ template <typename ArrayRefT> class BytesView {
using ConstBytesView = BytesView<ArrayRef<Byte>>;
using MutableBytesView = BytesView<MutableArrayRef<Byte>>;
+class MaterializedConstant : public AnyValue {
+ bool Cacheable;
+
+public:
+ MaterializedConstant(std::nullopt_t) : Cacheable(false) {}
+ MaterializedConstant(AnyValue V, bool Cacheable)
+ : AnyValue(std::move(V)), Cacheable(Cacheable) {}
+
+ bool isCacheable() const { return Cacheable; }
+};
+
/// The global context for the interpreter.
/// It tracks global state such as heap memory objects and floating point
/// environment.
@@ -290,7 +301,10 @@ class Context {
// Constants
// Use std::map to avoid iterator/reference invalidation.
- std::map<Constant *, AnyValue> ConstCache;
+ std::map<Constant *, MaterializedConstant> ConstCache;
+ // Temporary buffer for non-cacheable constants (e.g.,
+ // undef/ptrtoint/inttoptr).
+ SpecificBumpPtrAllocator<MaterializedConstant> NoncacheableConstBuffer;
DenseMap<Function *, Pointer> FuncAddrMap;
DenseMap<BasicBlock *, Pointer> BlockAddrMap;
DenseMap<uint64_t, std::pair<Function *, IntrusiveRefCntPtr<MemoryObject>>>
@@ -298,8 +312,8 @@ class Context {
DenseMap<uint64_t, std::pair<BasicBlock *, IntrusiveRefCntPtr<MemoryObject>>>
ValidBlockTargets;
DenseMap<GlobalVariable *, Pointer> GlobalAddrMap;
- std::optional<AnyValue> getConstantValueImpl(Constant *C);
- std::optional<AnyValue> evaluateConstantExpression(ConstantExpr *CE);
+ MaterializedConstant getConstantValueImpl(Constant *C);
+ MaterializedConstant evaluateConstantExpression(ConstantExpr *CE);
// Floating-point environment
RoundingMode CurrentRoundingMode = RoundingMode::NearestTiesToEven;
@@ -361,7 +375,12 @@ class Context {
uint64_t getEffectiveTypeAllocSize(Type *Ty);
uint64_t getEffectiveTypeStoreSize(Type *Ty);
- const AnyValue *getConstantValue(Constant *C);
+ /// Returns a pointer to an evaluated constant \p C. If it cannot be
+ /// evaluated, returns nullptr. Note that it returns a pointer to a temporary
+ /// buffer when \p C is not context-free. The caller is responsible for
+ /// calling resetNoncacheableConstantBuffer after all references are dropped.
+ const MaterializedConstant *getConstantValue(Constant *C);
+ void resetNoncacheableConstantBuffer();
IntrusiveRefCntPtr<MemoryObject> allocate(uint64_t Size, uint64_t Align,
StringRef Name, unsigned AS,
MemInitKind InitKind,
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 61059371dcb58..6b1cb817c85cb 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -2401,6 +2401,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
Instruction &I = *Top.PC;
visit(&I);
+ Ctx.resetNoncacheableConstantBuffer();
if (hasProgramExited())
break;
>From 69ea69c5d1bb6d80ece3fd07eb623e3459d0727d Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 28 Jun 2026 00:17:03 +0800
Subject: [PATCH 4/5] [llubi] Address review comments.
---
llvm/tools/llubi/lib/Context.cpp | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index b9315e70a4c0f..bd7c966a1c7e9 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -83,6 +83,7 @@ bool Context::initGlobalValues() {
return false;
store(*Obj, 0, *InitVal, GV.getValueType());
+ resetNoncacheableConstantBuffer();
}
return true;
}
@@ -173,7 +174,7 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
unsigned Opc = CE->getOpcode();
switch (Opc) {
case Instruction::Trunc: {
- const MaterializedConstant *Src = getConstantValue(CE->getOperand(0));
+ const auto *Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
if (Src->isPoison())
@@ -191,7 +192,7 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
}
case Instruction::BitCast: {
Constant *SrcOp = CE->getOperand(0);
- auto Src = getConstantValue(SrcOp);
+ const auto *Src = getConstantValue(SrcOp);
if (!Src)
return std::nullopt;
SmallVector<Byte> Bytes;
@@ -201,13 +202,13 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
Src->isCacheable());
}
case Instruction::InsertElement: {
- auto Src = getConstantValue(CE->getOperand(0));
+ const auto *Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
- auto Val = getConstantValue(CE->getOperand(1));
+ const auto *Val = getConstantValue(CE->getOperand(1));
if (!Val)
return std::nullopt;
- auto Idx = getConstantValue(CE->getOperand(2));
+ const auto *Idx = getConstantValue(CE->getOperand(2));
if (!Idx)
return std::nullopt;
auto &SrcVec = Src->asAggregate();
@@ -221,10 +222,10 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
return MaterializedConstant(std::move(ResVec), Cacheable);
}
case Instruction::ExtractElement: {
- auto Src = getConstantValue(CE->getOperand(0));
+ const auto *Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
- auto Idx = getConstantValue(CE->getOperand(1));
+ const auto *Idx = getConstantValue(CE->getOperand(1));
if (!Idx)
return std::nullopt;
auto &SrcVec = Src->asAggregate();
@@ -236,10 +237,10 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
Cacheable);
}
case Instruction::ShuffleVector: {
- auto LHS = getConstantValue(CE->getOperand(0));
+ const auto *LHS = getConstantValue(CE->getOperand(0));
if (!LHS)
return std::nullopt;
- auto RHS = getConstantValue(CE->getOperand(1));
+ const auto *RHS = getConstantValue(CE->getOperand(1));
if (!RHS)
return std::nullopt;
auto &LHSVec = LHS->asAggregate();
@@ -275,7 +276,7 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
bool Cacheable = true;
AnyValue Res =
computeGEP(*cast<GEPOperator>(CE), [&](Value *V) -> const AnyValue & {
- auto Val = getConstantValue(cast<Constant>(V));
+ const auto *Val = getConstantValue(cast<Constant>(V));
if (Val) {
Cacheable &= Val->isCacheable();
return *Val;
@@ -288,7 +289,7 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
return MaterializedConstant(std::move(Res), Cacheable);
}
case Instruction::PtrToAddr: {
- auto Src = getConstantValue(CE->getOperand(0));
+ const auto *Src = getConstantValue(CE->getOperand(0));
if (!Src)
return std::nullopt;
if (Src->isPoison())
@@ -310,10 +311,10 @@ MaterializedConstant Context::evaluateConstantExpression(ConstantExpr *CE) {
return std::nullopt;
default:
assert(Instruction::isBinaryOp(Opc) && "Must be binary operator?");
- auto *LHS = getConstantValue(CE->getOperand(0));
+ const auto *LHS = getConstantValue(CE->getOperand(0));
if (!LHS)
return std::nullopt;
- auto *RHS = getConstantValue(CE->getOperand(1));
+ const auto *RHS = getConstantValue(CE->getOperand(1));
if (!RHS)
return std::nullopt;
@@ -369,7 +370,7 @@ const MaterializedConstant *Context::getConstantValue(Constant *C) {
if (!Val.isCacheable()) {
assert(NoncacheableConstBuffer.getBytesAllocated() <=
1024 * sizeof(MaterializedConstant) &&
- "");
+ "Unbounded temporary buffer.");
return new (NoncacheableConstBuffer.Allocate())
MaterializedConstant(std::move(Val));
}
>From ed862716357fc72d35cf078d657e6e808d7ab359 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 28 Jun 2026 00:21:24 +0800
Subject: [PATCH 5/5] [Support] Fix missing interface
---
llvm/include/llvm/Support/Allocator.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index f58f73227a2a9..100f59efd82a9 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -470,6 +470,8 @@ template <typename T> class SpecificBumpPtrAllocator {
std::optional<int64_t> identifyObject(const void *Ptr) {
return Allocator.identifyObject(Ptr);
}
+
+ size_t getBytesAllocated() const { return Allocator.getBytesAllocated(); }
};
} // end namespace llvm
More information about the llvm-commits
mailing list