[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