[llvm] [NFCI][IR] Add DataLayout-aware `isZeroValue`/`getNullValue` and `getZeroValue` APIs (PR #183208)
Shilei Tian via llvm-commits
llvm-commits at lists.llvm.org
Sat May 2 08:18:47 PDT 2026
https://github.com/shiltian updated https://github.com/llvm/llvm-project/pull/183208
>From 2ce499bac234348d538b7dba13458fa1d475f867 Mon Sep 17 00:00:00 2001
From: Shilei Tian <i at tianshilei.me>
Date: Sat, 2 May 2026 10:58:40 -0400
Subject: [PATCH] [NFCI][IR] Add DataLayout pointer to zero and null value
related APIs
When the semantics of `ConstantPointerNull` change to represent a semantic null
pointer in the future, a null value won't necessarily be a zero value anymore.
Because of that, the entire LLVM constant infrastructure will need to change. As
a first step, this PR adds an optional data layout pointer to `isNullValue`,
`isZeroValue`, `getNullValue`, and `getZeroValue`. It isn't used yet, since a
null value is still a zero value right now.
---
llvm/include/llvm/IR/Constant.h | 27 +++++-
llvm/lib/IR/Constants.cpp | 18 +++-
llvm/unittests/IR/ConstantsTest.cpp | 144 ++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/IR/Constant.h b/llvm/include/llvm/IR/Constant.h
index 82a570e8a1446..f0a106f9f7ad9 100644
--- a/llvm/include/llvm/IR/Constant.h
+++ b/llvm/include/llvm/IR/Constant.h
@@ -22,6 +22,7 @@ namespace llvm {
class ConstantRange;
class APInt;
+class DataLayout;
/// This is an important base class in LLVM. It provides the common facilities
/// of all constant values in an LLVM program. A constant is a value that is
@@ -52,7 +53,15 @@ class Constant : public User {
Constant(const Constant &) = delete;
/// Return true if this is the value that would be returned by getNullValue.
- LLVM_ABI bool isNullValue() const;
+ /// TODO: Currently equivalent to isZeroValue(). Will diverge once the
+ /// semantics of ConstantPointerNull changes.
+ LLVM_ABI bool isNullValue(const DataLayout *DL = nullptr) const;
+
+ /// Return true iff this constant has an all-zero bit pattern.
+ /// TODO: For pointer types, it currently returns true if it is
+ /// ConstantPointerNull, but this will change once the semantics of
+ /// ConstantPointerNull changes to represent the semantic null pointer.
+ LLVM_ABI bool isZeroValue(const DataLayout *DL = nullptr) const;
/// Returns true if the value is one.
LLVM_ABI bool isOneValue() const;
@@ -187,7 +196,21 @@ class Constant : public User {
///
LLVM_ABI void handleOperandChange(Value *, Value *);
- LLVM_ABI static Constant *getNullValue(Type *Ty);
+ /// Constructor to create a null constant of arbitrary type.
+ /// TODO: Currently equivalent to getZeroValue(). Will diverge once the
+ /// semantics of ConstantPointerNull changes: for pointer types in address
+ /// spaces with non-zero null, getNullValue() will return the semantic null
+ /// pointer (ConstantPointerNull) while getZeroValue() will continue to return
+ /// the all-zero-bits value.
+ LLVM_ABI static Constant *getNullValue(Type *Ty,
+ const DataLayout *DL = nullptr);
+
+ /// Return the all-zero-bits constant for the given type.
+ /// TODO: For pointer types, it returns ConstantPointerNull but this will
+ /// change once the semantics of ConstantPointerNull changes to represent the
+ /// semantic null pointer.
+ LLVM_ABI static Constant *getZeroValue(Type *Ty,
+ const DataLayout *DL = nullptr);
/// @returns the value for an integer or vector of integer constant of the
/// given type that has all its bits set to true.
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp
index f07ce527c1240..e55534d8dbb38 100644
--- a/llvm/lib/IR/Constants.cpp
+++ b/llvm/lib/IR/Constants.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/ConstantFold.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
@@ -71,7 +72,13 @@ bool Constant::isNegativeZeroValue() const {
return isNullValue();
}
-bool Constant::isNullValue() const {
+bool Constant::isNullValue(const DataLayout *DL) const {
+ // TODO: when the semantics of ConstantPointerNull changes, a null value is
+ // not necessarily a zero value.
+ return isZeroValue(DL);
+}
+
+bool Constant::isZeroValue(const DataLayout *DL) const {
// 0 is null.
if (const ConstantInt *CI = dyn_cast<ConstantInt>(this))
return CI->isZero();
@@ -386,8 +393,13 @@ bool Constant::containsConstantExpression() const {
return false;
}
-/// Constructor to create a '0' constant of arbitrary type.
-Constant *Constant::getNullValue(Type *Ty) {
+Constant *Constant::getNullValue(Type *Ty, const DataLayout *DL) {
+ // TODO: when the semantics of ConstantPointerNull changes, a null value is
+ // not necessarily a zero value.
+ return getZeroValue(Ty, DL);
+}
+
+Constant *Constant::getZeroValue(Type *Ty, const DataLayout *DL) {
switch (Ty->getTypeID()) {
case Type::ByteTyID:
return ConstantByte::get(Ty, 0);
diff --git a/llvm/unittests/IR/ConstantsTest.cpp b/llvm/unittests/IR/ConstantsTest.cpp
index 34898aa467788..5d4a9cd3e5f95 100644
--- a/llvm/unittests/IR/ConstantsTest.cpp
+++ b/llvm/unittests/IR/ConstantsTest.cpp
@@ -10,6 +10,7 @@
#include "llvm-c/Core.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/ConstantFold.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
@@ -868,5 +869,148 @@ TEST(ConstantsTest, Float128Test) {
LLVMContextDispose(C);
}
+TEST(ConstantsTest, ZeroValueAPIs) {
+ LLVMContext Context;
+
+ Type *Int32Ty = Type::getInt32Ty(Context);
+ Type *FloatTy = Type::getFloatTy(Context);
+ Type *DoubleTy = Type::getDoubleTy(Context);
+ Type *PtrTy = PointerType::get(Context, /*AddressSpace=*/0);
+ Type *Ptr1Ty = PointerType::get(Context, /*AddressSpace=*/1);
+ StructType *StructTy = StructType::get(Int32Ty, PtrTy);
+ StructType *StructWithAS1PtrTy = StructType::get(Int32Ty, Ptr1Ty);
+ ArrayType *ArrayTy = ArrayType::get(Int32Ty, /*NumElements=*/4);
+
+ Constant *IntZero = ConstantInt::get(Int32Ty, 0);
+ Constant *IntOne = ConstantInt::get(Int32Ty, 1);
+ Constant *FPZero = ConstantFP::get(FloatTy, 0.0);
+ Constant *FPNegZero = ConstantFP::get(FloatTy, -0.0);
+ Constant *FPOne = ConstantFP::get(FloatTy, 1.0);
+ Constant *PtrNull0 = ConstantPointerNull::get(cast<PointerType>(PtrTy));
+ Constant *PtrNull1 = ConstantPointerNull::get(cast<PointerType>(Ptr1Ty));
+ Constant *CAZ = ConstantAggregateZero::get(StructTy);
+
+ // TODO: Null and zero are still the same value. The DataLayout argument is
+ // accepted by the API but is not used yet.
+ EXPECT_EQ(Constant::getZeroValue(Int32Ty), Constant::getNullValue(Int32Ty));
+ EXPECT_EQ(Constant::getZeroValue(FloatTy), Constant::getNullValue(FloatTy));
+ EXPECT_EQ(Constant::getZeroValue(PtrTy), Constant::getNullValue(PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(Ptr1Ty), Constant::getNullValue(Ptr1Ty));
+ EXPECT_EQ(Constant::getZeroValue(StructTy), Constant::getNullValue(StructTy));
+ EXPECT_EQ(Constant::getZeroValue(StructWithAS1PtrTy),
+ Constant::getNullValue(StructWithAS1PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(ArrayTy), Constant::getNullValue(ArrayTy));
+
+ EXPECT_TRUE(IntZero->isZeroValue());
+ EXPECT_FALSE(IntOne->isZeroValue());
+ EXPECT_TRUE(FPZero->isZeroValue());
+ // -0.0 has a non-zero bit pattern (sign bit set), so it is NOT a zero value.
+ EXPECT_FALSE(FPNegZero->isZeroValue());
+ EXPECT_FALSE(FPOne->isZeroValue());
+ EXPECT_TRUE(PtrNull0->isZeroValue());
+ EXPECT_TRUE(PtrNull1->isZeroValue());
+ EXPECT_TRUE(CAZ->isZeroValue());
+
+ // -0.0 is NOT zero (sign bit set = non-zero bit pattern).
+ // Verify consistency with isNullValue: both agree +0.0 is zero, -0.0 is not.
+ EXPECT_TRUE(FPZero->isNullValue());
+ EXPECT_FALSE(FPNegZero->isNullValue());
+ EXPECT_TRUE(FPZero->isZeroValue());
+ EXPECT_FALSE(FPNegZero->isZeroValue());
+
+ Constant *DblZero = ConstantFP::get(DoubleTy, 0.0);
+ Constant *DblNegZero = ConstantFP::get(DoubleTy, -0.0);
+ EXPECT_TRUE(DblZero->isZeroValue());
+ EXPECT_FALSE(DblNegZero->isZeroValue());
+
+ // Vector splats of FP zeros.
+ Constant *VecPosZero =
+ ConstantVector::getSplat(ElementCount::getFixed(2), FPZero);
+ Constant *VecNegZero =
+ ConstantVector::getSplat(ElementCount::getFixed(2), FPNegZero);
+ // Splat of +0.0 collapses to CAZ, which is zero.
+ EXPECT_TRUE(isa<ConstantAggregateZero>(VecPosZero));
+ EXPECT_TRUE(VecPosZero->isZeroValue());
+ // Splat of -0.0 does NOT collapse to CAZ and is NOT zero.
+ EXPECT_FALSE(isa<ConstantAggregateZero>(VecNegZero));
+ EXPECT_FALSE(VecNegZero->isZeroValue());
+
+ DataLayout DefaultDL("");
+ DataLayout AllOnesDL("po1:64:64");
+
+ EXPECT_TRUE(IntZero->isZeroValue(&DefaultDL));
+ EXPECT_FALSE(IntOne->isZeroValue(&DefaultDL));
+ EXPECT_TRUE(FPZero->isZeroValue(&DefaultDL));
+ EXPECT_FALSE(FPNegZero->isZeroValue(&DefaultDL));
+ EXPECT_FALSE(FPOne->isZeroValue(&DefaultDL));
+ EXPECT_TRUE(PtrNull0->isZeroValue(&DefaultDL));
+ EXPECT_TRUE(PtrNull1->isZeroValue(&DefaultDL));
+ EXPECT_TRUE(CAZ->isZeroValue(&DefaultDL));
+
+ // TODO: Even though AS 1 has an all-ones null pointer representation in this
+ // layout, ConstantPointerNull is still treated as both null and zero.
+ EXPECT_TRUE(PtrNull0->isZeroValue(&AllOnesDL));
+ EXPECT_TRUE(PtrNull1->isZeroValue(&AllOnesDL));
+ EXPECT_TRUE(PtrNull0->isNullValue(&AllOnesDL));
+ EXPECT_TRUE(PtrNull1->isNullValue(&AllOnesDL));
+ EXPECT_TRUE(IntZero->isZeroValue(&AllOnesDL));
+ EXPECT_TRUE(FPZero->isZeroValue(&AllOnesDL));
+ EXPECT_TRUE(CAZ->isZeroValue(&AllOnesDL));
+
+ EXPECT_EQ(Constant::getNullValue(Int32Ty, /*DL=*/nullptr),
+ Constant::getNullValue(Int32Ty));
+ EXPECT_EQ(Constant::getZeroValue(Int32Ty, /*DL=*/nullptr),
+ Constant::getZeroValue(Int32Ty));
+ EXPECT_EQ(Constant::getNullValue(PtrTy, /*DL=*/nullptr),
+ Constant::getNullValue(PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(PtrTy, /*DL=*/nullptr),
+ Constant::getZeroValue(PtrTy));
+ EXPECT_EQ(Constant::getNullValue(StructTy, /*DL=*/nullptr),
+ Constant::getNullValue(StructTy));
+ EXPECT_EQ(Constant::getZeroValue(StructTy, /*DL=*/nullptr),
+ Constant::getZeroValue(StructTy));
+
+ EXPECT_EQ(Constant::getNullValue(Int32Ty, &DefaultDL),
+ Constant::getNullValue(Int32Ty));
+ EXPECT_EQ(Constant::getZeroValue(Int32Ty, &DefaultDL),
+ Constant::getZeroValue(Int32Ty));
+ EXPECT_EQ(Constant::getNullValue(PtrTy, &DefaultDL),
+ Constant::getNullValue(PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(PtrTy, &DefaultDL),
+ Constant::getZeroValue(PtrTy));
+ EXPECT_EQ(Constant::getNullValue(StructTy, &DefaultDL),
+ Constant::getNullValue(StructTy));
+ EXPECT_EQ(Constant::getZeroValue(StructTy, &DefaultDL),
+ Constant::getZeroValue(StructTy));
+ EXPECT_EQ(Constant::getNullValue(ArrayTy, &DefaultDL),
+ Constant::getNullValue(ArrayTy));
+ EXPECT_EQ(Constant::getZeroValue(ArrayTy, &DefaultDL),
+ Constant::getZeroValue(ArrayTy));
+
+ // TODO: The DataLayout argument does not affect getNullValue() or
+ // getZeroValue() yet, even for pointer-containing types in an address space
+ // with a non-zero null pointer value.
+ EXPECT_EQ(Constant::getNullValue(Int32Ty, &AllOnesDL),
+ Constant::getNullValue(Int32Ty));
+ EXPECT_EQ(Constant::getZeroValue(Int32Ty, &AllOnesDL),
+ Constant::getZeroValue(Int32Ty));
+ EXPECT_EQ(Constant::getNullValue(PtrTy, &AllOnesDL),
+ Constant::getNullValue(PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(PtrTy, &AllOnesDL),
+ Constant::getZeroValue(PtrTy));
+ EXPECT_EQ(Constant::getNullValue(Ptr1Ty, &AllOnesDL),
+ Constant::getNullValue(Ptr1Ty));
+ EXPECT_EQ(Constant::getZeroValue(Ptr1Ty, &AllOnesDL),
+ Constant::getZeroValue(Ptr1Ty));
+ EXPECT_EQ(Constant::getNullValue(StructTy, &AllOnesDL),
+ Constant::getNullValue(StructTy));
+ EXPECT_EQ(Constant::getZeroValue(StructTy, &AllOnesDL),
+ Constant::getZeroValue(StructTy));
+ EXPECT_EQ(Constant::getNullValue(StructWithAS1PtrTy, &AllOnesDL),
+ Constant::getNullValue(StructWithAS1PtrTy));
+ EXPECT_EQ(Constant::getZeroValue(StructWithAS1PtrTy, &AllOnesDL),
+ Constant::getZeroValue(StructWithAS1PtrTy));
+}
+
} // end anonymous namespace
} // end namespace llvm
More information about the llvm-commits
mailing list