[llvm] a02ef60 - [llubi] Add support for undef values (#205602)

via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 27 10:38:42 PDT 2026


Author: Yingwei Zheng
Date: 2026-06-28T01:38:36+08:00
New Revision: a02ef60ff46c1a9c99cce6ca159229dc2f6c5ff2

URL: https://github.com/llvm/llvm-project/commit/a02ef60ff46c1a9c99cce6ca159229dc2f6c5ff2
DIFF: https://github.com/llvm/llvm-project/commit/a02ef60ff46c1a9c99cce6ca159229dc2f6c5ff2.diff

LOG: [llubi] Add support for undef values (#205602)

Although we are planning to deprecate the undef value, it is still
widely used in the intermediate results of the pipeline, which blocks
the pass bisection. This patch uses `freeze poison` as a refinement of
undef.

Note that the undef value evaluates to different values each time the
user is executed. So it cannot be cached like other constants. A
temporary buffer is introduced to take ownership of these values and
avoid breaking the interface (although this is a bit ugly...). This will
also be used by a follow-up patch for ptrtoint/inttoptr.

>From my experience, it is enough for test case reduction of middle-end
miscompilation bugs (there are still counterexamples like
https://github.com/dtcxzyw/llvm-autoreduce/issues/61). However, when
processing backend miscompilation bugs, lli typically uses a garbage
value, so that llvm-reduce may produce an invalid result. I think we may
need to introduce two flags to migrate this issue: one for poisoning
uninitialized memory, and another for diagnosing immediately when
non-deterministic behaviour occurs. This would prevent llvm-reduce from
converting a deterministic repro into a non-deterministic one.

`llvm/test/tools/llubi/unsupported_constant.ll` is also dropped because
it is annoying to pick another unimplemented constant value...

Added: 
    llvm/test/tools/llubi/undef.ll

Modified: 
    llvm/include/llvm/Support/Allocator.h
    llvm/tools/llubi/lib/Context.cpp
    llvm/tools/llubi/lib/Context.h
    llvm/tools/llubi/lib/Interpreter.cpp

Removed: 
    llvm/test/tools/llubi/unsupported_constant.ll


################################################################################
diff  --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 92027cceea3bf..ea8e8a829ee55 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -472,6 +472,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

diff  --git a/llvm/test/tools/llubi/undef.ll b/llvm/test/tools/llubi/undef.ll
new file mode 100644
index 0000000000000..4e28ce89f1738
--- /dev/null
+++ b/llvm/test/tools/llubi/undef.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+ at g = global i32 undef
+
+define void @main() {
+  %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:   %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/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..bd7c966a1c7e9 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -83,66 +83,86 @@ bool Context::initGlobalValues() {
       return false;
 
     store(*Obj, 0, *InitVal, GV.getValueType());
+    resetNoncacheableConstantBuffer();
   }
   return true;
 }
 
-std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
+MaterializedConstant Context::getConstantValueImpl(Constant *C) {
   if (isa<PoisonValue>(C))
-    return AnyValue::getPoisonValue(*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);
@@ -150,69 +170,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 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->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);
+    const 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));
+    const auto *Src = getConstantValue(CE->getOperand(0));
     if (!Src)
       return std::nullopt;
-    const AnyValue *Val = getConstantValue(CE->getOperand(1));
+    const auto *Val = getConstantValue(CE->getOperand(1));
     if (!Val)
       return std::nullopt;
-    const AnyValue *Idx = getConstantValue(CE->getOperand(2));
+    const 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));
+    const auto *Src = getConstantValue(CE->getOperand(0));
     if (!Src)
       return std::nullopt;
-    const AnyValue *Idx = getConstantValue(CE->getOperand(1));
+    const 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));
+    const auto *LHS = getConstantValue(CE->getOperand(0));
     if (!LHS)
       return std::nullopt;
-    const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+    const auto *RHS = getConstantValue(CE->getOperand(1));
     if (!RHS)
       return std::nullopt;
     auto &LHSVec = LHS->asAggregate();
@@ -237,40 +265,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)
+          const 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));
+    const 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:
@@ -278,10 +311,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));
+    const auto *LHS = getConstantValue(CE->getOperand(0));
     if (!LHS)
       return std::nullopt;
-    const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+    const auto *RHS = getConstantValue(CE->getOperand(1));
     if (!RHS)
       return std::nullopt;
 
@@ -310,6 +343,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();
@@ -317,23 +352,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) &&
+           "Unbounded temporary buffer.");
+    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 7c1e8e82c7e74..d8e3450c4accd 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -202,6 +202,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.
@@ -291,7 +302,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>>>
@@ -299,8 +313,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;
@@ -362,7 +376,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 fd39b809d8f25..8bf88464d0178 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -2574,6 +2574,7 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
 
         Instruction &I = *Top.PC;
         visit(&I);
+        Ctx.resetNoncacheableConstantBuffer();
         if (hasProgramExited())
           break;
 


        


More information about the llvm-commits mailing list