summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Analysis/ConstantFolding.cpp
diff options
context:
space:
mode:
authorShilei Tian <i@tianshilei.me>2025-11-15 23:19:05 -0500
committerShilei Tian <i@tianshilei.me>2025-11-17 00:16:27 -0500
commitc52a3b6b8b1cd3fa4c7e58d4d27265482ea837d2 (patch)
treef9a9b09e2f9a9e9f12706b6d6761dcecb99b3432 /llvm/lib/Analysis/ConstantFolding.cpp
parent49d5bb0ad0cb31410184c462801c5049ad671517 (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/Analysis/ConstantFolding.cpp')
-rwxr-xr-xllvm/lib/Analysis/ConstantFolding.cpp41
1 files changed, 40 insertions, 1 deletions
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index da32542cf787..adb09e37c4cc 100755
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1497,6 +1497,21 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
llvm_unreachable("Missing case");
case Instruction::PtrToAddr:
case Instruction::PtrToInt:
+ // If the input is a nullptr, we can fold it to the corresponding nullptr
+ // value.
+ if (Opcode == Instruction::PtrToInt && C->isNullValue()) {
+ if (std::optional<APInt> NullPtrValue = DL.getNullPtrValue(
+ C->getType()->getScalarType()->getPointerAddressSpace())) {
+ if (NullPtrValue->isZero()) {
+ return Constant::getZeroValue(DestTy);
+ } else if (NullPtrValue->isAllOnes()) {
+ return ConstantInt::get(
+ DestTy, NullPtrValue->zextOrTrunc(DestTy->getScalarSizeInBits()));
+ } else {
+ llvm_unreachable("invalid nullptr value");
+ }
+ }
+ }
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
Constant *FoldedValue = nullptr;
// If the input is an inttoptr, eliminate the pair. This requires knowing
@@ -1543,6 +1558,13 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
}
break;
case Instruction::IntToPtr:
+ // We can fold it to a null pointer if the input is the nullptr value.
+ if (std::optional<APInt> NullPtrValue = DL.getNullPtrValue(
+ DestTy->getScalarType()->getPointerAddressSpace())) {
+ if ((NullPtrValue->isZero() && C->isZeroValue()) ||
+ (NullPtrValue->isAllOnes() && C->isAllOnesValue()))
+ return Constant::getNullValue(DestTy);
+ }
// If the input is a ptrtoint, turn the pair into a ptr to ptr bitcast if
// the int size is >= the ptr size and the address spaces are the same.
// This requires knowing the width of a pointer, so it can't be done in
@@ -1561,6 +1583,24 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
}
}
break;
+ case Instruction::AddrSpaceCast:
+ // A null pointer (`ptr addrspace(N) null` in IR presentation,
+ // `ConstantPointerNull` in LLVM class, not `nullptr` in C/C++) used to
+ // represent a zero-value pointer in the corresponding address space.
+ // Therefore, we can't simply fold an address space cast of a null pointer
+ // from one address space to another, because on some targets, the nullptr
+ // of an address space could be non-zero.
+ //
+ // Recently, the semantic of `ptr addrspace(N) null` is changed to represent
+ // the actual nullptr in the corresponding address space. It can be zero or
+ // non-zero, depending on the target. Therefore, we can fold an address
+ // space cast of a nullptr from one address space to another.
+
+ // If the input is a nullptr, we can fold it to the corresponding
+ // nullptr in the destination address space.
+ if (C->isNullValue())
+ return Constant::getNullValue(DestTy);
+ [[fallthrough]];
case Instruction::Trunc:
case Instruction::ZExt:
case Instruction::SExt:
@@ -1570,7 +1610,6 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
case Instruction::SIToFP:
case Instruction::FPToUI:
case Instruction::FPToSI:
- case Instruction::AddrSpaceCast:
break;
case Instruction::BitCast:
return FoldBitCast(C, DestTy, DL);