[llvm] [llubi] Add support for constant expressions (PR #203746)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 14 03:22:32 PDT 2026
https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/203746
>From b24fa26a2709fe982bf6bc4cb706d4d7681faa96 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 8 Jun 2026 09:47:23 +0800
Subject: [PATCH 1/2] [llubi] Add support for constant expressions
---
.../llubi/global_constexpr_initializer.ll | 108 +++++-
llvm/test/tools/llubi/unsupported_constant.ll | 12 +
llvm/tools/llubi/lib/Context.cpp | 346 +++++++++++++++++-
llvm/tools/llubi/lib/Context.h | 13 +
llvm/tools/llubi/lib/Interpreter.cpp | 204 +----------
llvm/tools/llubi/lib/Value.h | 36 ++
6 files changed, 513 insertions(+), 206 deletions(-)
create mode 100644 llvm/test/tools/llubi/unsupported_constant.ll
diff --git a/llvm/test/tools/llubi/global_constexpr_initializer.ll b/llvm/test/tools/llubi/global_constexpr_initializer.ll
index c4b2ee2e78384..ebc7cc9997aa6 100644
--- a/llvm/test/tools/llubi/global_constexpr_initializer.ll
+++ b/llvm/test/tools/llubi/global_constexpr_initializer.ll
@@ -1,10 +1,114 @@
-; RUN: not llubi < %s 2>&1 | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+target datalayout = "p:64:64:64"
@value = global i32 0
@aggregate = global [1 x ptr] [ptr getelementptr (i32, ptr @value, i64 1)]
+ at trunc = global i8 trunc (i64 ptrtoaddr (ptr @value to i64) to i8)
+ at bitcast = global double bitcast (i64 ptrtoaddr (ptr @value to i64) to double)
+ at insert = global <2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7))
+ at extract = global ptr extractelement (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 1)
+ at shuffle = global <2 x ptr> shufflevector (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), <2 x ptr> poison, <2 x i32> <i32 1, i32 1>)
+ at gep = global ptr getelementptr (i32, ptr @value, i64 1)
+ at add = global i64 add (i64 ptrtoaddr (ptr @value to i64), i64 8)
+ at sub = global i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8)
+ at xor = global i64 xor (i64 ptrtoaddr (ptr @value to i64), i64 8)
define void @main() {
+ %load_aggregate = load ptr, ptr @aggregate
+ %val_aggregage = getelementptr i8, ptr getelementptr (i32, ptr @value, i64 1), i64 0
+
+ %load_trunc = load i8, ptr @trunc
+ %val_trunc = add i8 trunc (i64 ptrtoaddr (ptr @value to i64) to i8), 0
+ %val_trunc_poison = add i8 trunc (i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9) to i8), 0
+ %val_trunc_vec = add <2 x i8> trunc (<2 x i64> insertelement (<2 x i64> poison, i64 ptrtoaddr (ptr @value to i64), i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)) to <2 x i8>), zeroinitializer
+
+ %load_bitcast = load double, ptr @bitcast
+ %val_bitcast = fadd double bitcast (i64 ptrtoaddr (ptr @value to i64) to double), -0.0
+ %load_insert = load <2 x ptr>, ptr @insert
+
+ %val_insert = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 0
+ %val_insert_poison1 = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9)), i64 0
+ %val_insert_poison2 = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 6)), i64 0
+ %val_insert_scalable = getelementptr i8, <vscale x 2 x ptr> insertelement (<vscale x 2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8)), i64 0
+
+ %load_extract = load ptr, ptr @extract
+ %val_extract = getelementptr i8, ptr extractelement (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 1), i64 0
+ %val_extract_poison1 = getelementptr i8, ptr extractelement (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 1), i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9)), i64 0
+ %val_extract_poison2 = getelementptr i8, ptr extractelement (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 1), i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 6)), i64 0
+ %val_extract_scalable = getelementptr i8, ptr extractelement (<vscale x 2 x ptr> insertelement (<vscale x 2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8)), i64 1), i64 0
+
+ %load_shuffle = load <2 x ptr>, ptr @shuffle
+ %val_shuffle = getelementptr i8, <2 x ptr> shufflevector (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), <2 x ptr> poison, <2 x i32> <i32 1, i32 1>), i64 0
+ %val_shuffle_mixed = getelementptr i8, <2 x ptr> shufflevector (<2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), <2 x ptr> zeroinitializer, <2 x i32> <i32 1, i32 2>), i64 0
+
+ %load_gep = load ptr, ptr @gep
+ %val_gep = getelementptr i8, ptr getelementptr (i32, ptr @value, i64 1), i64 0
+ %val_gep_inbounds = getelementptr i8, ptr getelementptr inbounds (i32, ptr @value, i64 1), i64 0
+ %val_gep_inbounds_oob = getelementptr i8, ptr getelementptr inbounds (i32, ptr @value, i64 2), i64 0
+ %val_gep_vec1 = getelementptr i8, <2 x ptr> getelementptr (i32, ptr @value, <2 x i64> <i64 1, i64 2>), i64 0
+ %val_gep_vec2 = getelementptr i8, <2 x ptr> getelementptr (i32, <2 x ptr> insertelement (<2 x ptr> zeroinitializer, ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 1), i64 0
+
+ %load_add = load i64, ptr @add
+ %val_add = add i64 add (i64 ptrtoaddr (ptr @value to i64), i64 8), 0
+ %val_add_nuw_nsw = add i64 add nuw nsw (i64 ptrtoaddr (ptr @value to i64), i64 8), 0
+ %val_add_nsw_poison = add i64 add nsw (i64 ptrtoaddr (ptr @value to i64), i64 9223372036854775800), 0
+ %val_add_nuw_poison = add i64 add nuw (i64 ptrtoaddr (ptr @value to i64), i64 -7), 0
+
+ %load_sub = load i64, ptr @sub
+ %val_sub = add i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8), 0
+ %val_sub_nuw_nsw = add i64 sub nuw nsw (i64 ptrtoaddr (ptr @value to i64), i64 8), 0
+ %val_sub_nsw_poison = add i64 sub nsw (i64 ptrtoaddr (ptr @value to i64), i64 -9223372036854775800), 0
+ %val_sub_nuw_poison = add i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9), 0
+
+ %load_xor = load i64, ptr @xor
+ %val_xor = add i64 xor (i64 ptrtoaddr (ptr @value to i64), i64 8), 0
+ %val_xor_vec_poison = add <2 x i64> xor (<2 x i64> insertelement (<2 x i64> zeroinitializer, i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9), i64 1), <2 x i64> splat(i64 1)), zeroinitializer
+
ret void
}
-; CHECK: error: Failed to initialize global values
+; CHECK: Entering function: main
+; CHECK-NEXT: %load_aggregate = load ptr, ptr @aggregate, align 8 => ptr 0xC [@value + 4]
+; CHECK-NEXT: %val_aggregage = getelementptr i8, ptr getelementptr (i32, ptr @value, i64 1), i64 0 => ptr 0xC [@value + 4]
+; CHECK-NEXT: %load_trunc = load i8, ptr @trunc, align 1 => i8 8
+; CHECK-NEXT: %val_trunc = add i8 trunc (i64 ptrtoaddr (ptr @value to i64) to i8), 0 => i8 8
+; CHECK-NEXT: %val_trunc_poison = add i8 trunc (i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9) to i8), 0 => poison
+; CHECK-NEXT: %val_trunc_vec = add <2 x i8> <i8 trunc (i64 extractelement (<2 x i64> insertelement (<2 x i64> poison, i64 ptrtoaddr (ptr @value to i64), i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i32 0) to i8), i8 trunc (i64 extractelement (<2 x i64> insertelement (<2 x i64> poison, i64 ptrtoaddr (ptr @value to i64), i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i32 1) to i8)>, zeroinitializer => { poison, i8 8 }
+; CHECK-NEXT: %load_bitcast = load double, ptr @bitcast, align 8 => double 3.952530e-323
+; CHECK-NEXT: %val_bitcast = fadd double bitcast (i64 ptrtoaddr (ptr @value to i64) to double), -0.000000e+00 => double 3.952530e-323
+; CHECK-NEXT: %load_insert = load <2 x ptr>, ptr @insert, align 16 => { ptr 0x0 [nullary], ptr 0x8 [@value] }
+; CHECK-NEXT: %val_insert = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 0 => { ptr 0x0 [nullary], ptr 0x8 [@value] }
+; CHECK-NEXT: %val_insert_poison1 = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9)), i64 0 => { poison, poison }
+; CHECK-NEXT: %val_insert_poison2 = getelementptr i8, <2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 6)), i64 0 => { poison, poison }
+; CHECK-NEXT: %val_insert_scalable = getelementptr i8, <vscale x 2 x ptr> insertelement (<vscale x 2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8)), i64 0 => { ptr 0x8 [@value], ptr 0x0 [nullary], ptr 0x0 [nullary], ptr 0x0 [nullary], ptr 0x0 [nullary], ptr 0x0 [nullary], ptr 0x0 [nullary], ptr 0x0 [nullary] }
+; CHECK-NEXT: %load_extract = load ptr, ptr @extract, align 8 => ptr 0x8 [@value]
+; CHECK-NEXT: %val_extract = getelementptr i8, ptr extractelement (<2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i64 1), i64 0 => ptr 0x8 [@value]
+; CHECK-NEXT: %val_extract_poison1 = getelementptr i8, ptr extractelement (<2 x ptr> <ptr null, ptr @value>, i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9)), i64 0 => poison
+; CHECK-NEXT: %val_extract_poison2 = getelementptr i8, ptr extractelement (<2 x ptr> <ptr null, ptr @value>, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 6)), i64 0 => poison
+; CHECK-NEXT: %val_extract_scalable = getelementptr i8, ptr extractelement (<vscale x 2 x ptr> insertelement (<vscale x 2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8)), i64 1), i64 0 => ptr 0x0 [nullary]
+; CHECK-NEXT: %load_shuffle = load <2 x ptr>, ptr @shuffle, align 16 => { ptr 0x8 [@value], ptr 0x8 [@value] }
+; CHECK-NEXT: %val_shuffle = getelementptr i8, <2 x ptr> <ptr extractelement (<2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i32 1), ptr extractelement (<2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i32 1)>, i64 0 => { ptr 0x8 [@value], ptr 0x8 [@value] }
+; CHECK-NEXT: %val_shuffle_mixed = getelementptr i8, <2 x ptr> <ptr extractelement (<2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), i32 1), ptr null>, i64 0 => { ptr 0x8 [@value], ptr 0x0 [nullary] }
+; CHECK-NEXT: %load_gep = load ptr, ptr @gep, align 8 => ptr 0xC [@value + 4]
+; CHECK-NEXT: %val_gep = getelementptr i8, ptr getelementptr (i32, ptr @value, i64 1), i64 0 => ptr 0xC [@value + 4]
+; CHECK-NEXT: %val_gep_inbounds = getelementptr i8, ptr getelementptr inbounds (i32, ptr @value, i64 1), i64 0 => ptr 0xC [@value + 4]
+; CHECK-NEXT: %val_gep_inbounds_oob = getelementptr i8, ptr getelementptr inbounds (i32, ptr @value, i64 2), i64 0 => poison
+; CHECK-NEXT: %val_gep_vec1 = getelementptr i8, <2 x ptr> getelementptr (i32, ptr @value, <2 x i64> <i64 1, i64 2>), i64 0 => { ptr 0xC [@value + 4], ptr 0x10 [@value + 8] }
+; CHECK-NEXT: %val_gep_vec2 = getelementptr i8, <2 x ptr> getelementptr (i32, <2 x ptr> insertelement (<2 x ptr> splat (ptr null), ptr @value, i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 7)), <2 x i64> splat (i64 1)), i64 0 => { ptr 0x4 [nullary], ptr 0xC [@value + 4] }
+; CHECK-NEXT: %load_add = load i64, ptr @add, align 4 => i64 16
+; CHECK-NEXT: %val_add = add i64 add (i64 ptrtoaddr (ptr @value to i64), i64 8), 0 => i64 16
+; CHECK-NEXT: %val_add_nuw_nsw = add i64 add nuw nsw (i64 ptrtoaddr (ptr @value to i64), i64 8), 0 => i64 16
+; CHECK-NEXT: %val_add_nsw_poison = add i64 add nsw (i64 ptrtoaddr (ptr @value to i64), i64 9223372036854775800), 0 => poison
+; CHECK-NEXT: %val_add_nuw_poison = add i64 add nuw (i64 ptrtoaddr (ptr @value to i64), i64 -7), 0 => poison
+; CHECK-NEXT: %load_sub = load i64, ptr @sub, align 4 => i64 0
+; CHECK-NEXT: %val_sub = add i64 sub (i64 ptrtoaddr (ptr @value to i64), i64 8), 0 => i64 0
+; CHECK-NEXT: %val_sub_nuw_nsw = add i64 sub nuw nsw (i64 ptrtoaddr (ptr @value to i64), i64 8), 0 => i64 0
+; CHECK-NEXT: %val_sub_nsw_poison = add i64 sub nsw (i64 ptrtoaddr (ptr @value to i64), i64 -9223372036854775800), 0 => poison
+; CHECK-NEXT: %val_sub_nuw_poison = add i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9), 0 => poison
+; CHECK-NEXT: %load_xor = load i64, ptr @xor, align 4 => i64 0
+; CHECK-NEXT: %val_xor = add i64 xor (i64 ptrtoaddr (ptr @value to i64), i64 8), 0 => i64 0
+; CHECK-NEXT: %val_xor_vec_poison = add <2 x i64> <i64 1, i64 xor (i64 sub nuw (i64 ptrtoaddr (ptr @value to i64), i64 9), i64 1)>, zeroinitializer => { i64 1, poison }
+; 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
new file mode 100644
index 0000000000000..96d23cff4904a
--- /dev/null
+++ b/llvm/test/tools/llubi/unsupported_constant.ll
@@ -0,0 +1,12 @@
+; 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 7b9d0d849e5f3..570ae980dbd5c 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//
#include "Context.h"
+#include "llvm/IR/GetElementPtrTypeIterator.h"
+#include "llvm/IR/Instructions.h"
#include "llvm/Support/MathExtras.h"
namespace llvm::ubi {
@@ -89,12 +91,9 @@ std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
if (isa<PoisonValue>(C))
return AnyValue::getPoisonValue(*this, C->getType());
- if (isa<ConstantAggregateZero>(C))
+ if (isa<ConstantAggregateZero, ConstantPointerNull>(C))
return AnyValue::getNullValue(*this, C->getType());
- if (isa<ConstantPointerNull>(C))
- return Pointer::null(C->getType()->getPointerAddressSpace(), DL);
-
if (auto *CI = dyn_cast<ConstantInt>(C)) {
if (auto *VecTy = dyn_cast<VectorType>(CI->getType()))
return std::vector<AnyValue>(getEVL(VecTy->getElementCount()),
@@ -142,9 +141,182 @@ std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
if (auto *F = dyn_cast<Function>(C))
return FuncAddrMap.at(F);
+ if (auto *CE = dyn_cast<ConstantExpr>(C))
+ return evaluateConstantExpression(CE);
+
return std::nullopt;
}
+std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
+ unsigned Opc = CE->getOpcode();
+ switch (Opc) {
+ case Instruction::Trunc: {
+ const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ if (!Src)
+ return std::nullopt;
+ if (Src->isPoison())
+ return AnyValue::poison();
+ unsigned BitWidth = CE->getType()->getScalarSizeInBits();
+ if (Src->isInteger())
+ return AnyValue(Src->asInteger().trunc(BitWidth));
+ std::vector<AnyValue> Vec = Src->asAggregate();
+ for (auto &V : Vec) {
+ if (V.isInteger())
+ V = V.asInteger().trunc(BitWidth);
+ }
+ return AnyValue(std::move(Vec));
+ }
+ case Instruction::BitCast: {
+ Constant *SrcOp = CE->getOperand(0);
+ const AnyValue *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());
+ }
+ case Instruction::InsertElement: {
+ const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ if (!Src)
+ return std::nullopt;
+ const AnyValue *Val = getConstantValue(CE->getOperand(1));
+ if (!Val)
+ return std::nullopt;
+ const AnyValue *Idx = getConstantValue(CE->getOperand(2));
+ if (!Idx)
+ return std::nullopt;
+ auto &SrcVec = Src->asAggregate();
+ if (Idx->isPoison() || Idx->asInteger().uge(SrcVec.size()))
+ return AnyValue::getPoisonValue(*this, CE->getType());
+ std::vector<AnyValue> ResVec = SrcVec;
+ ResVec[Idx->asInteger().getZExtValue()] = *Val;
+ return AnyValue(std::move(ResVec));
+ }
+ case Instruction::ExtractElement: {
+ const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ if (!Src)
+ return std::nullopt;
+ const AnyValue *Idx = getConstantValue(CE->getOperand(1));
+ if (!Idx)
+ return std::nullopt;
+ auto &SrcVec = Src->asAggregate();
+ if (Idx->isPoison() || Idx->asInteger().uge(SrcVec.size()))
+ return AnyValue::getPoisonValue(*this, CE->getType());
+ return SrcVec[Idx->asInteger().getZExtValue()];
+ }
+ case Instruction::ShuffleVector: {
+ const AnyValue *LHS = getConstantValue(CE->getOperand(0));
+ if (!LHS)
+ return std::nullopt;
+ const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+ if (!RHS)
+ return std::nullopt;
+ auto &LHSVec = LHS->asAggregate();
+ auto &RHSVec = RHS->asAggregate();
+ uint32_t Size = cast<VectorType>(CE->getOperand(0)->getType())
+ ->getElementCount()
+ .getKnownMinValue();
+ std::vector<AnyValue> Res;
+ uint32_t DstLen =
+ getEVL(cast<VectorType>(CE->getType())->getElementCount());
+ Res.reserve(DstLen);
+ uint32_t Stride = CE->getShuffleMask().size();
+ // For scalable vectors, we need to repeat the shuffle mask until we fill
+ // the destination vector.
+ for (uint32_t Off = 0; Off != DstLen; Off += Stride) {
+ for (int Idx : CE->getShuffleMask()) {
+ if (Idx == PoisonMaskElem)
+ Res.push_back(AnyValue::poison());
+ else if (Idx < static_cast<int>(Size))
+ Res.push_back(LHSVec[Idx]);
+ else
+ Res.push_back(RHSVec[Idx - Size]);
+ }
+ }
+ return AnyValue(std::move(Res));
+ }
+ 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;
+ AnyValue Res =
+ computeGEP(*cast<GEPOperator>(CE), [&](Value *V) -> const AnyValue & {
+ const AnyValue *Val = getConstantValue(cast<Constant>(V));
+ if (Val)
+ return *Val;
+ PoisonValue = AnyValue::getPoisonValue(*this, V->getType());
+ return PoisonValue;
+ });
+ if (!PoisonValue.isNone())
+ return std::nullopt;
+ return std::move(Res);
+ }
+ case Instruction::PtrToAddr: {
+ const AnyValue *Src = getConstantValue(CE->getOperand(0));
+ if (!Src)
+ return std::nullopt;
+ if (Src->isPoison())
+ return AnyValue::poison();
+ unsigned BitWidth = CE->getType()->getScalarSizeInBits();
+ if (Src->isPointer())
+ return Src->asPointer().address().trunc(BitWidth);
+ std::vector<AnyValue> Vec = Src->asAggregate();
+ for (auto &V : Vec) {
+ if (V.isPointer())
+ V = V.asPointer().address().trunc(BitWidth);
+ }
+ return AnyValue(std::move(Vec));
+ }
+ default:
+ assert(Instruction::isBinaryOp(Opc) && "Must be binary operator?");
+ const AnyValue *LHS = getConstantValue(CE->getOperand(0));
+ if (!LHS)
+ return std::nullopt;
+ const AnyValue *RHS = getConstantValue(CE->getOperand(1));
+ if (!RHS)
+ return std::nullopt;
+
+ bool HasNUW = false;
+ bool HasNSW = false;
+ if (auto *OBO = dyn_cast<OverflowingBinaryOperator>(CE)) {
+ HasNUW = OBO->hasNoUnsignedWrap();
+ HasNSW = OBO->hasNoSignedWrap();
+ }
+
+ auto ScalarEval = [&](const AnyValue &LHS,
+ const AnyValue &RHS) -> AnyValue {
+ if (LHS.isPoison() || RHS.isPoison())
+ return AnyValue::poison();
+ auto &LHSVal = LHS.asInteger();
+ auto &RHSVal = RHS.asInteger();
+ switch (Opc) {
+ case Instruction::Add:
+ return addNoWrap(LHSVal, RHSVal, HasNSW, HasNUW);
+ case Instruction::Sub:
+ return subNoWrap(LHSVal, RHSVal, HasNSW, HasNUW);
+ case Instruction::Xor:
+ return LHSVal ^ RHSVal;
+ default:
+ llvm_unreachable("Unsupported opcode in constant expression.");
+ }
+ };
+
+ if (CE->getType()->isVectorTy()) {
+ auto &LHSVec = LHS->asAggregate();
+ auto &RHSVec = RHS->asAggregate();
+ std::vector<AnyValue> ResVec;
+ ResVec.reserve(LHSVec.size());
+ for (const auto &[ScalarLHS, ScalarRHS] : zip(LHSVec, RHSVec))
+ ResVec.push_back(ScalarEval(ScalarLHS, ScalarRHS));
+ return std::move(ResVec);
+ }
+
+ return ScalarEval(*LHS, *RHS);
+ }
+}
+
const AnyValue *Context::getConstantValue(Constant *C) {
auto It = ConstCache.find(C);
if (It != ConstCache.end())
@@ -547,6 +719,172 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
}
}
+AnyValue Context::computePtrAdd(const Pointer &Ptr, const APInt &Offset,
+ GEPNoWrapFlags Flags,
+ AnyValue &AccumulatedOffset) {
+ if (Offset.isZero())
+ return Ptr;
+ APInt IndexBits = Ptr.address().trunc(Offset.getBitWidth());
+ auto NewIndex =
+ addNoWrap(IndexBits, Offset, /*HasNSW=*/false, Flags.hasNoUnsignedWrap());
+ if (NewIndex.isPoison())
+ return AnyValue::poison();
+ if (Flags.hasNoUnsignedSignedWrap()) {
+ // The successive addition of the current address, truncated to the
+ // pointer index type and interpreted as an unsigned number, and each
+ // offset, interpreted as a signed number, does not wrap the pointer index
+ // type.
+ if (Offset.isNonNegative() ? NewIndex.asInteger().ult(IndexBits)
+ : NewIndex.asInteger().ugt(IndexBits))
+ return AnyValue::poison();
+ }
+ APInt NewAddr = Ptr.address();
+ NewAddr.insertBits(NewIndex.asInteger(), 0);
+
+ MemoryObject *MO = nullptr;
+ if (Flags.isInBounds()) {
+ MO = checkProvenance(
+ Ptr, [](const Provenance &) { return true; },
+ /*HasSideEffect=*/false);
+ if (!MO || !MO->inBounds(NewAddr))
+ return AnyValue::poison();
+ }
+
+ if (!AccumulatedOffset.isPoison()) {
+ AccumulatedOffset =
+ addNoWrap(AccumulatedOffset.asInteger(), Offset,
+ Flags.hasNoUnsignedSignedWrap(), Flags.hasNoUnsignedWrap());
+ if (AccumulatedOffset.isPoison())
+ return AnyValue::poison();
+ }
+
+ // Should not expose provenance here even if the new address doesn't point
+ // to the original object.
+ auto Res = Ptr.getWithNewAddr(NewAddr);
+ if (MO) {
+ auto &Prov = Res.provenance();
+ if (Prov.isWildcard() && !Prov.getMemoryObject())
+ Res = Res.getWithNewProvenance(Prov.getWithKnownMemoryObject(*MO));
+ }
+ return Res;
+}
+
+AnyValue Context::computePtrAdd(const AnyValue &Ptr, const APInt &Offset,
+ GEPNoWrapFlags Flags,
+ AnyValue &AccumulatedOffset) {
+ if (Ptr.isPoison())
+ return AnyValue::poison();
+ return computePtrAdd(Ptr.asPointer(), Offset, Flags, AccumulatedOffset);
+}
+
+AnyValue Context::computeScaledPtrAdd(const AnyValue &Ptr,
+ const AnyValue &Index, const APInt &Scale,
+ GEPNoWrapFlags Flags,
+ AnyValue &AccumulatedOffset) {
+ if (Ptr.isPoison() || Index.isPoison())
+ return AnyValue::poison();
+ assert(Ptr.isPointer() && Index.isInteger() && "Unexpected type.");
+ if (Scale.isOne())
+ return computePtrAdd(Ptr, Index.asInteger(), Flags, AccumulatedOffset);
+ auto ScaledOffset =
+ mulNoWrap(Index.asInteger(), Scale, Flags.hasNoUnsignedSignedWrap(),
+ Flags.hasNoUnsignedWrap());
+ if (ScaledOffset.isPoison())
+ return AnyValue::poison();
+ return computePtrAdd(Ptr, ScaledOffset.asInteger(), Flags, AccumulatedOffset);
+}
+
+static AnyValue canonicalizeIndex(const AnyValue &Idx, unsigned IndexBitWidth,
+ GEPNoWrapFlags Flags) {
+ if (Idx.isPoison())
+ return AnyValue::poison();
+ auto &IdxInt = Idx.asInteger();
+ if (IdxInt.getBitWidth() == IndexBitWidth)
+ return Idx;
+ if (IdxInt.getBitWidth() > IndexBitWidth) {
+ if (Flags.hasNoUnsignedSignedWrap() && !IdxInt.isSignedIntN(IndexBitWidth))
+ return AnyValue::poison();
+
+ if (Flags.hasNoUnsignedWrap() && !IdxInt.isIntN(IndexBitWidth))
+ return AnyValue::poison();
+
+ return IdxInt.trunc(IndexBitWidth);
+ }
+ return IdxInt.sext(IndexBitWidth);
+}
+
+AnyValue
+Context::computeGEP(GEPOperator &GEP,
+ function_ref<const AnyValue &(Value *V)> GetValue) {
+ uint32_t IndexBitWidth =
+ DL.getIndexSizeInBits(GEP.getType()->getPointerAddressSpace());
+ GEPNoWrapFlags Flags = GEP.getNoWrapFlags();
+ AnyValue Res = GetValue(GEP.getPointerOperand());
+ AnyValue AccumulatedOffset = APInt(IndexBitWidth, 0);
+ if (Res.isAggregate())
+ AccumulatedOffset =
+ AnyValue::getVectorSplat(AccumulatedOffset, Res.asAggregate().size());
+ auto ApplyScaledOffset = [&](const AnyValue &Index, const APInt &Scale) {
+ if (Index.isAggregate() && !Res.isAggregate()) {
+ Res = AnyValue::getVectorSplat(Res, Index.asAggregate().size());
+ AccumulatedOffset = AnyValue::getVectorSplat(AccumulatedOffset,
+ Index.asAggregate().size());
+ }
+ if (Index.isAggregate() && Res.isAggregate()) {
+ for (auto &&[ResElem, IndexElem, OffsetElem] :
+ zip(Res.asAggregate(), Index.asAggregate(),
+ AccumulatedOffset.asAggregate()))
+ ResElem = computeScaledPtrAdd(
+ ResElem, canonicalizeIndex(IndexElem, IndexBitWidth, Flags), Scale,
+ Flags, OffsetElem);
+ } else {
+ AnyValue CanonicalIndex = canonicalizeIndex(Index, IndexBitWidth, Flags);
+ if (Res.isAggregate()) {
+ for (auto &&[ResElem, OffsetElem] :
+ zip(Res.asAggregate(), AccumulatedOffset.asAggregate()))
+ ResElem = computeScaledPtrAdd(ResElem, CanonicalIndex, Scale, Flags,
+ OffsetElem);
+ } else {
+ Res = computeScaledPtrAdd(Res, CanonicalIndex, Scale, Flags,
+ AccumulatedOffset);
+ }
+ }
+ };
+
+ for (gep_type_iterator GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP);
+ GTI != GTE; ++GTI) {
+ Value *V = GTI.getOperand();
+
+ // Fast path for zero offsets.
+ if (auto *CI = dyn_cast<ConstantInt>(V)) {
+ if (CI->isZero())
+ continue;
+ }
+ if (isa<ConstantAggregateZero>(V))
+ continue;
+
+ // Handle a struct index, which adds its field offset to the pointer.
+ if (StructType *STy = GTI.getStructTypeOrNull()) {
+ unsigned ElementIdx = cast<ConstantInt>(V)->getZExtValue();
+ const StructLayout *SL = DL.getStructLayout(STy);
+ // Element offset is in bytes.
+ ApplyScaledOffset(APInt(IndexBitWidth, SL->getElementOffset(ElementIdx)),
+ APInt(IndexBitWidth, 1));
+ continue;
+ }
+
+ // Truncate if type size exceeds index space.
+ // TODO: Should be documented in LangRef: GEPs with nowrap flags should
+ // return poison when the type size exceeds index space.
+ TypeSize Offset = GTI.getSequentialElementStride(DL);
+ APInt Scale(IndexBitWidth, getEffectiveTypeSize(Offset),
+ /*isSigned=*/false, /*implicitTrunc=*/true);
+ if (!Scale.isZero())
+ ApplyScaledOffset(GetValue(V), Scale);
+ }
+ return Res;
+}
+
MemoryObject::~MemoryObject() = default;
MemoryObject::MemoryObject(uint64_t Addr, uint64_t Size, StringRef Name,
unsigned AS, MemInitKind InitKind,
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index cc947bd6af51a..5f8b79a6b183c 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -15,6 +15,7 @@
#include "llvm/AsmParser/AsmParserContext.h"
#include "llvm/IR/FPEnv.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/Operator.h"
#include <map>
#include <optional>
#include <random>
@@ -279,6 +280,14 @@ class Context {
void toBytes(const AnyValue &Val, Type *Ty, uint32_t OffsetInBits,
MutableBytesView Bytes, bool PaddingBits);
+ AnyValue computePtrAdd(const Pointer &Ptr, const APInt &Offset,
+ GEPNoWrapFlags Flags, AnyValue &AccumulatedOffset);
+ AnyValue computePtrAdd(const AnyValue &Ptr, const APInt &Offset,
+ GEPNoWrapFlags Flags, AnyValue &AccumulatedOffset);
+ AnyValue computeScaledPtrAdd(const AnyValue &Ptr, const AnyValue &Index,
+ const APInt &Scale, GEPNoWrapFlags Flags,
+ AnyValue &AccumulatedOffset);
+
// Constants
// Use std::map to avoid iterator/reference invalidation.
std::map<Constant *, AnyValue> ConstCache;
@@ -290,6 +299,7 @@ class Context {
ValidBlockTargets;
DenseMap<GlobalVariable *, Pointer> GlobalAddrMap;
std::optional<AnyValue> getConstantValueImpl(Constant *C);
+ std::optional<AnyValue> evaluateConstantExpression(ConstantExpr *CE);
// Floating-point environment
RoundingMode CurrentRoundingMode = RoundingMode::NearestTiesToEven;
@@ -397,6 +407,9 @@ class Context {
/// Freeze the value in-place.
void freeze(AnyValue &Val, Type *Ty);
+ AnyValue computeGEP(GEPOperator &GEP,
+ function_ref<const AnyValue &(Value *V)> GetValue);
+
Function *getTargetFunction(const Pointer &Ptr);
BasicBlock *getTargetBlock(const Pointer &Ptr);
diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 341bf8fd7fca9..5769d0a4a9c4d 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -17,7 +17,6 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/VectorUtils.h"
-#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Operator.h"
@@ -31,42 +30,6 @@ namespace llvm::ubi {
using namespace PatternMatch;
-static AnyValue addNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
- bool HasNUW) {
- APInt Res = LHS + RHS;
- if (HasNUW && Res.ult(RHS))
- return AnyValue::poison();
- if (HasNSW && LHS.isNonNegative() == RHS.isNonNegative() &&
- LHS.isNonNegative() != Res.isNonNegative())
- return AnyValue::poison();
- return Res;
-}
-
-static AnyValue subNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
- bool HasNUW) {
- APInt Res = LHS - RHS;
- if (HasNUW && Res.ugt(LHS))
- return AnyValue::poison();
- if (HasNSW && LHS.isNonNegative() != RHS.isNonNegative() &&
- LHS.isNonNegative() != Res.isNonNegative())
- return AnyValue::poison();
- return Res;
-}
-
-static AnyValue mulNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
- bool HasNUW) {
- bool Overflow = false;
- APInt Res = LHS.smul_ov(RHS, Overflow);
- if (HasNSW && Overflow)
- return AnyValue::poison();
- if (HasNUW) {
- (void)LHS.umul_ov(RHS, Overflow);
- if (Overflow)
- return AnyValue::poison();
- }
- return Res;
-}
-
/// Visit the scalar values recursively. The callback function may modify the
/// value in-place.
static void forEachScalarValue(AnyValue &V,
@@ -713,99 +676,6 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
return false;
}
- AnyValue computePtrAdd(const Pointer &Ptr, const APInt &Offset,
- GEPNoWrapFlags Flags, AnyValue &AccumulatedOffset) {
- if (Offset.isZero())
- return Ptr;
- APInt IndexBits = Ptr.address().trunc(Offset.getBitWidth());
- auto NewIndex = addNoWrap(IndexBits, Offset, /*HasNSW=*/false,
- Flags.hasNoUnsignedWrap());
- if (NewIndex.isPoison())
- return AnyValue::poison();
- if (Flags.hasNoUnsignedSignedWrap()) {
- // The successive addition of the current address, truncated to the
- // pointer index type and interpreted as an unsigned number, and each
- // offset, interpreted as a signed number, does not wrap the pointer index
- // type.
- if (Offset.isNonNegative() ? NewIndex.asInteger().ult(IndexBits)
- : NewIndex.asInteger().ugt(IndexBits))
- return AnyValue::poison();
- }
- APInt NewAddr = Ptr.address();
- NewAddr.insertBits(NewIndex.asInteger(), 0);
-
- MemoryObject *MO = nullptr;
- if (Flags.isInBounds()) {
- MO = Ctx.checkProvenance(
- Ptr, [](const Provenance &) { return true; },
- /*HasSideEffect=*/false);
- if (!MO || !MO->inBounds(NewAddr))
- return AnyValue::poison();
- }
-
- if (!AccumulatedOffset.isPoison()) {
- AccumulatedOffset =
- addNoWrap(AccumulatedOffset.asInteger(), Offset,
- Flags.hasNoUnsignedSignedWrap(), Flags.hasNoUnsignedWrap());
- if (AccumulatedOffset.isPoison())
- return AnyValue::poison();
- }
-
- // Should not expose provenance here even if the new address doesn't point
- // to the original object.
- auto Res = Ptr.getWithNewAddr(NewAddr);
- if (MO) {
- auto &Prov = Res.provenance();
- if (Prov.isWildcard() && !Prov.getMemoryObject())
- Res = Res.getWithNewProvenance(Prov.getWithKnownMemoryObject(*MO));
- }
- return Res;
- }
-
- AnyValue computePtrAdd(const AnyValue &Ptr, const APInt &Offset,
- GEPNoWrapFlags Flags, AnyValue &AccumulatedOffset) {
- if (Ptr.isPoison())
- return AnyValue::poison();
- return computePtrAdd(Ptr.asPointer(), Offset, Flags, AccumulatedOffset);
- }
-
- AnyValue computeScaledPtrAdd(const AnyValue &Ptr, const AnyValue &Index,
- const APInt &Scale, GEPNoWrapFlags Flags,
- AnyValue &AccumulatedOffset) {
- if (Ptr.isPoison() || Index.isPoison())
- return AnyValue::poison();
- assert(Ptr.isPointer() && Index.isInteger() && "Unexpected type.");
- if (Scale.isOne())
- return computePtrAdd(Ptr, Index.asInteger(), Flags, AccumulatedOffset);
- auto ScaledOffset =
- mulNoWrap(Index.asInteger(), Scale, Flags.hasNoUnsignedSignedWrap(),
- Flags.hasNoUnsignedWrap());
- if (ScaledOffset.isPoison())
- return AnyValue::poison();
- return computePtrAdd(Ptr, ScaledOffset.asInteger(), Flags,
- AccumulatedOffset);
- }
-
- AnyValue canonicalizeIndex(const AnyValue &Idx, unsigned IndexBitWidth,
- GEPNoWrapFlags Flags) {
- if (Idx.isPoison())
- return AnyValue::poison();
- auto &IdxInt = Idx.asInteger();
- if (IdxInt.getBitWidth() == IndexBitWidth)
- return Idx;
- if (IdxInt.getBitWidth() > IndexBitWidth) {
- if (Flags.hasNoUnsignedSignedWrap() &&
- !IdxInt.isSignedIntN(IndexBitWidth))
- return AnyValue::poison();
-
- if (Flags.hasNoUnsignedWrap() && !IdxInt.isIntN(IndexBitWidth))
- return AnyValue::poison();
-
- return IdxInt.trunc(IndexBitWidth);
- }
- return IdxInt.sext(IndexBitWidth);
- }
-
DenormalMode getCurrentDenormalMode(Type *Ty) {
return CurrentFrame->Func.getDenormalMode(
Ty->getScalarType()->getFltSemantics());
@@ -2361,76 +2231,10 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
}
void visitGetElementPtrInst(GetElementPtrInst &GEP) {
- uint32_t IndexBitWidth =
- DL.getIndexSizeInBits(GEP.getType()->getPointerAddressSpace());
- GEPNoWrapFlags Flags = GEP.getNoWrapFlags();
- AnyValue Res = getValue(GEP.getPointerOperand());
- AnyValue AccumulatedOffset = APInt(IndexBitWidth, 0);
- if (Res.isAggregate())
- AccumulatedOffset =
- AnyValue::getVectorSplat(AccumulatedOffset, Res.asAggregate().size());
- auto ApplyScaledOffset = [&](const AnyValue &Index, const APInt &Scale) {
- if (Index.isAggregate() && !Res.isAggregate()) {
- Res = AnyValue::getVectorSplat(Res, Index.asAggregate().size());
- AccumulatedOffset = AnyValue::getVectorSplat(
- AccumulatedOffset, Index.asAggregate().size());
- }
- if (Index.isAggregate() && Res.isAggregate()) {
- for (auto &&[ResElem, IndexElem, OffsetElem] :
- zip(Res.asAggregate(), Index.asAggregate(),
- AccumulatedOffset.asAggregate()))
- ResElem = computeScaledPtrAdd(
- ResElem, canonicalizeIndex(IndexElem, IndexBitWidth, Flags),
- Scale, Flags, OffsetElem);
- } else {
- AnyValue CanonicalIndex =
- canonicalizeIndex(Index, IndexBitWidth, Flags);
- if (Res.isAggregate()) {
- for (auto &&[ResElem, OffsetElem] :
- zip(Res.asAggregate(), AccumulatedOffset.asAggregate()))
- ResElem = computeScaledPtrAdd(ResElem, CanonicalIndex, Scale, Flags,
- OffsetElem);
- } else {
- Res = computeScaledPtrAdd(Res, CanonicalIndex, Scale, Flags,
- AccumulatedOffset);
- }
- }
- };
-
- for (gep_type_iterator GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP);
- GTI != GTE; ++GTI) {
- Value *V = GTI.getOperand();
-
- // Fast path for zero offsets.
- if (auto *CI = dyn_cast<ConstantInt>(V)) {
- if (CI->isZero())
- continue;
- }
- if (isa<ConstantAggregateZero>(V))
- continue;
-
- // Handle a struct index, which adds its field offset to the pointer.
- if (StructType *STy = GTI.getStructTypeOrNull()) {
- unsigned ElementIdx = cast<ConstantInt>(V)->getZExtValue();
- const StructLayout *SL = DL.getStructLayout(STy);
- // Element offset is in bytes.
- ApplyScaledOffset(
- APInt(IndexBitWidth, SL->getElementOffset(ElementIdx)),
- APInt(IndexBitWidth, 1));
- continue;
- }
-
- // Truncate if type size exceeds index space.
- // TODO: Should be documented in LangRef: GEPs with nowrap flags should
- // return poison when the type size exceeds index space.
- TypeSize Offset = GTI.getSequentialElementStride(DL);
- APInt Scale(IndexBitWidth, Ctx.getEffectiveTypeSize(Offset),
- /*isSigned=*/false, /*implicitTrunc=*/true);
- if (!Scale.isZero())
- ApplyScaledOffset(getValue(V), Scale);
- }
-
- setResult(GEP, std::move(Res));
+ setResult(GEP, Ctx.computeGEP(cast<GEPOperator>(GEP),
+ [this](Value *V) -> const AnyValue & {
+ return getValue(V);
+ }));
}
void visitPtrToInt(PtrToIntInst &I) {
diff --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index b5c5078103e18..77e428813a832 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -334,6 +334,42 @@ inline raw_ostream &operator<<(raw_ostream &OS, const Pointer &P) {
return OS;
}
+inline AnyValue addNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
+ bool HasNUW) {
+ APInt Res = LHS + RHS;
+ if (HasNUW && Res.ult(RHS))
+ return AnyValue::poison();
+ if (HasNSW && LHS.isNonNegative() == RHS.isNonNegative() &&
+ LHS.isNonNegative() != Res.isNonNegative())
+ return AnyValue::poison();
+ return Res;
+}
+
+inline AnyValue subNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
+ bool HasNUW) {
+ APInt Res = LHS - RHS;
+ if (HasNUW && Res.ugt(LHS))
+ return AnyValue::poison();
+ if (HasNSW && LHS.isNonNegative() != RHS.isNonNegative() &&
+ LHS.isNonNegative() != Res.isNonNegative())
+ return AnyValue::poison();
+ return Res;
+}
+
+inline AnyValue mulNoWrap(const APInt &LHS, const APInt &RHS, bool HasNSW,
+ bool HasNUW) {
+ bool Overflow = false;
+ APInt Res = LHS.smul_ov(RHS, Overflow);
+ if (HasNSW && Overflow)
+ return AnyValue::poison();
+ if (HasNUW) {
+ (void)LHS.umul_ov(RHS, Overflow);
+ if (Overflow)
+ return AnyValue::poison();
+ }
+ return Res;
+}
+
} // namespace llvm::ubi
#endif
>From 9f055d55118a35cd82296e4bb7d8d46fef4a3b70 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 14 Jun 2026 18:22:08 +0800
Subject: [PATCH 2/2] [llubi] Address review comments.
---
llvm/tools/llubi/lib/Context.cpp | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 570ae980dbd5c..988faa94a59db 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -91,7 +91,10 @@ std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
if (isa<PoisonValue>(C))
return AnyValue::getPoisonValue(*this, C->getType());
- if (isa<ConstantAggregateZero, ConstantPointerNull>(C))
+ if (isa<ConstantAggregateZero>(C))
+ return AnyValue::getNullValue(*this, C->getType());
+
+ if (isa<ConstantPointerNull>(C))
return AnyValue::getNullValue(*this, C->getType());
if (auto *CI = dyn_cast<ConstantInt>(C)) {
@@ -269,6 +272,10 @@ std::optional<AnyValue> Context::evaluateConstantExpression(ConstantExpr *CE) {
}
return AnyValue(std::move(Vec));
}
+ case Instruction::PtrToInt:
+ case Instruction::IntToPtr:
+ case Instruction::AddrSpaceCast:
+ return std::optional;
default:
assert(Instruction::isBinaryOp(Opc) && "Must be binary operator?");
const AnyValue *LHS = getConstantValue(CE->getOperand(0));
More information about the llvm-commits
mailing list