[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