[llvm] [llubi] Add support for constant expressions (PR #203746)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 14 03:07:25 PDT 2026


================
@@ -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?");
----------------
nikic wrote:

We should explicitly list the supported binary opcodes here and return nullptr for other cases (so that ptrtoint expressions don't assert).

https://github.com/llvm/llvm-project/pull/203746


More information about the llvm-commits mailing list