diff options
| author | Shilei Tian <i@tianshilei.me> | 2025-11-15 23:19:05 -0500 |
|---|---|---|
| committer | Shilei Tian <i@tianshilei.me> | 2025-11-17 00:16:27 -0500 |
| commit | c52a3b6b8b1cd3fa4c7e58d4d27265482ea837d2 (patch) | |
| tree | f9a9b09e2f9a9e9f12706b6d6761dcecb99b3432 /llvm/lib/IR/Constants.cpp | |
| parent | 49d5bb0ad0cb31410184c462801c5049ad671517 (diff) | |
[WIP][IR][Constants] Change the semantic of `ConstantPointerNull` to represent an actual `nullptr` instead of a zero-value pointerupstream/users/shiltian/ptr-zeroinitializer-to-inttoptr
The value of a `nullptr` is not always `0`. For example, on AMDGPU, the `nullptr` in address spaces 3 and 5 is `0xffffffff`. Currently, there is no target-independent way to get this information, making it difficult and error-prone to handle null pointers in target-agnostic code.
We do have `ConstantPointerNull`, but it might be a little confusing and misleading. It represents a pointer with an all-zero value rather than necessarily a real `nullptr`. Therefore, to represent a real `nullptr` in address space `N`, we need to use `addrspacecast ptr null to ptr addrspace(N)` and it can't be folded.
In this PR, we change the semantic of `ConstantPointerNull` to represent an actual `nullptr` instead of a zero-value pointer. Here is the detailed changes.
* `ptr addrspace(N) null` will represent the actual `nullptr` in address space `N`.
* `ptr addrspace(N) zeroinitializer` will represent a zero-value pointer in address space `N`.
* `Constant::getNullValue` will return a _null_ value. It is same as the current semantics except for the `PointerType`, which will return a real `nullptr` pointer.
* `Constant::getZeroValue` will return a zero value constant. It is completely same as the current semantics. To represent a zero-value pointer, a `ConstantExpr` will be used (effectively `inttoptr i8 0 to ptr addrspace(N)`).
* Correspondingly, there will be both `Constant::isNullValue` and `Constant::isZeroValue`.
The RFC is https://discourse.llvm.org/t/rfc-introduce-sentinel-pointer-value-to-datalayout/85265. It is a little bit old and the title might look different, but everything eventually converges to this change. An early attempt can be found in https://github.com/llvm/llvm-project/pull/131557, which has many valuable discussion as well.
This PR is still WIP but any early feedback is welcome. I'll include as many necessary code changes as possible in this PR, but eventually this needs to be carefully split into multiple PRs, and I'll do it after the changes look good to every one.
Diffstat (limited to 'llvm/lib/IR/Constants.cpp')
| -rw-r--r-- | llvm/lib/IR/Constants.cpp | 97 |
1 files changed, 88 insertions, 9 deletions
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index cbce8bd73610..87f2952ed5d2 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -72,17 +72,35 @@ bool Constant::isNegativeZeroValue() const { } // Return true iff this constant is positive zero (floating point), negative -// zero (floating point), or a null value. +// zero (floating point), zero-value pointer, or a null value. bool Constant::isZeroValue() const { + // We can no longer safely say that a ConstantPointerNull is a zero value. + if (isa<ConstantPointerNull>(this)) + return false; + // Floating point values have an explicit -0.0 value. if (const ConstantFP *CFP = dyn_cast<ConstantFP>(this)) return CFP->isZero(); + // Zero value pointer is a constant expression of inttoptr(0). + if (const auto *CE = dyn_cast<ConstantExpr>(this)) { + if (CE->getOpcode() == Instruction::IntToPtr) { + Constant *SrcCI = cast<Constant>(CE->getOperand(0)); + // We don't need to check the bitwise value since it is zext or truncate + // for inttoptr(0) so it doesn't matter. + if (SrcCI->isZeroValue()) + return true; + } + } + // Check for constant splat vectors of 1 values. if (getType()->isVectorTy()) if (const auto *SplatCFP = dyn_cast_or_null<ConstantFP>(getSplatValue())) return SplatCFP->isZero(); + if (isa<ConstantAggregateZero>(this)) + return true; + // Otherwise, just use +0.0. return isNullValue(); } @@ -100,8 +118,14 @@ bool Constant::isNullValue() const { // constant zero is zero for aggregates, cpnull is null for pointers, none for // tokens. - return isa<ConstantAggregateZero>(this) || isa<ConstantPointerNull>(this) || - isa<ConstantTokenNone>(this) || isa<ConstantTargetNone>(this); + if (isa<ConstantPointerNull>(this) || isa<ConstantTokenNone>(this) || + isa<ConstantTargetNone>(this)) + return true; + + if (auto *CA = dyn_cast<ConstantAggregate>(this)) + return CA->isNullValue(); + + return false; } bool Constant::isAllOnesValue() const { @@ -369,7 +393,10 @@ bool Constant::containsConstantExpression() const { return false; } -/// Constructor to create a '0' constant of arbitrary type. +/// Constructor that creates a null constant of any type. For most types, this +/// means a constant with value '0', but for pointer types, it represents a +/// nullptr constant. A nullptr isn't always a zero-value pointer in certain +/// address spaces on some targets. Constant *Constant::getNullValue(Type *Ty) { switch (Ty->getTypeID()) { case Type::IntegerTyID: @@ -386,10 +413,12 @@ Constant *Constant::getNullValue(Type *Ty) { case Type::PointerTyID: return ConstantPointerNull::get(cast<PointerType>(Ty)); case Type::StructTyID: + return ConstantStruct::getNullValue(cast<StructType>(Ty)); case Type::ArrayTyID: + return ConstantArray::getNullValue(cast<ArrayType>(Ty)); case Type::FixedVectorTyID: case Type::ScalableVectorTyID: - return ConstantAggregateZero::get(Ty); + return ConstantVector::getNullValue(cast<VectorType>(Ty)); case Type::TokenTyID: return ConstantTokenNone::get(Ty->getContext()); case Type::TargetExtTyID: @@ -400,6 +429,24 @@ Constant *Constant::getNullValue(Type *Ty) { } } +/// Constructor that creates a zero constant of any type. For most types, this +/// is equivalent to getNullValue. For pointer types, it creates an inttoptr +/// constant expression. +Constant *Constant::getZeroValue(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: + return ConstantExpr::getIntToPtr( + ConstantInt::get(Type::getInt8Ty(Ty->getContext()), 0), Ty); + case Type::StructTyID: + case Type::ArrayTyID: + case Type::FixedVectorTyID: + case Type::ScalableVectorTyID: + return ConstantAggregateZero::get(Ty); + default: + return Constant::getNullValue(Ty); + } +} + Constant *Constant::getIntegerValue(Type *Ty, const APInt &V) { Type *ScalarTy = Ty->getScalarType(); @@ -735,7 +782,7 @@ static bool constantIsDead(const Constant *C, bool RemoveDeadUsers) { ReplaceableMetadataImpl::SalvageDebugInfo(*C); const_cast<Constant *>(C)->destroyConstant(); } - + return true; } @@ -1307,6 +1354,19 @@ ConstantAggregate::ConstantAggregate(Type *T, ValueTy VT, } } +bool ConstantAggregate::isNullValue() const { + if (getType()->isVectorTy()) { + Constant *V = getSplatValue(); + return V && V->isNullValue(); + } + + for (unsigned I = 0; I < getNumOperands(); ++I) { + if (!getOperand(I)->isNullValue()) + return false; + } + return true; +} + ConstantArray::ConstantArray(ArrayType *T, ArrayRef<Constant *> V, AllocInfo AllocInfo) : ConstantAggregate(T, ConstantArrayVal, V, AllocInfo) { @@ -1392,11 +1452,11 @@ Constant *ConstantStruct::get(StructType *ST, ArrayRef<Constant*> V) { if (!V.empty()) { isUndef = isa<UndefValue>(V[0]); isPoison = isa<PoisonValue>(V[0]); - isZero = V[0]->isNullValue(); + isZero = V[0]->isZeroValue(); // PoisonValue inherits UndefValue, so its check is not necessary. if (isUndef || isZero) { for (Constant *C : V) { - if (!C->isNullValue()) + if (!C->isZeroValue()) isZero = false; if (!isa<PoisonValue>(C)) isPoison = false; @@ -1437,7 +1497,7 @@ Constant *ConstantVector::getImpl(ArrayRef<Constant*> V) { // If this is an all-undef or all-zero vector, return a // ConstantAggregateZero or UndefValue. Constant *C = V[0]; - bool isZero = C->isNullValue(); + bool isZero = C->isZeroValue(); bool isUndef = isa<UndefValue>(C); bool isPoison = isa<PoisonValue>(C); bool isSplatFP = UseConstantFPForFixedLengthSplat && isa<ConstantFP>(C); @@ -3329,6 +3389,12 @@ Value *ConstantArray::handleOperandChangeImpl(Value *From, Value *To) { Values, this, From, ToC, NumUpdated, OperandNo); } +Constant *ConstantArray::getNullValue(ArrayType *T) { + return ConstantArray::get( + T, SmallVector<Constant *>(T->getNumElements(), + Constant::getNullValue(T->getElementType()))); +} + Value *ConstantStruct::handleOperandChangeImpl(Value *From, Value *To) { assert(isa<Constant>(To) && "Cannot make Constant refer to non-constant!"); Constant *ToC = cast<Constant>(To); @@ -3365,6 +3431,14 @@ Value *ConstantStruct::handleOperandChangeImpl(Value *From, Value *To) { Values, this, From, ToC, NumUpdated, OperandNo); } +Constant *ConstantStruct::getNullValue(StructType *T) { + SmallVector<Constant *> Values; + Values.reserve(T->getNumElements()); + for (unsigned I = 0; I < T->getNumElements(); ++I) + Values.push_back(Constant::getNullValue(T->getElementType(I))); + return ConstantStruct::get(T, Values); +} + Value *ConstantVector::handleOperandChangeImpl(Value *From, Value *To) { assert(isa<Constant>(To) && "Cannot make Constant refer to non-constant!"); Constant *ToC = cast<Constant>(To); @@ -3391,6 +3465,11 @@ Value *ConstantVector::handleOperandChangeImpl(Value *From, Value *To) { Values, this, From, ToC, NumUpdated, OperandNo); } +Constant *ConstantVector::getNullValue(VectorType *T) { + return ConstantVector::getSplat(T->getElementCount(), + Constant::getNullValue(T->getElementType())); +} + Value *ConstantExpr::handleOperandChangeImpl(Value *From, Value *ToV) { assert(isa<Constant>(ToV) && "Cannot make Constant refer to non-constant!"); Constant *To = cast<Constant>(ToV); |
