Skip to content

Commit 9478a0d

Browse files
committed
[WIP][IR][Constants] Change the semantic of ConstantPointerNull to represent an actual nullptr instead of a zero-value pointer
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 #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.
1 parent c940bfd commit 9478a0d

File tree

63 files changed

+448
-310
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+448
-310
lines changed

clang/lib/Basic/Targets/AMDGPU.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static const char *const DataLayoutStringR600 =
3131
"-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1";
3232

3333
static const char *const DataLayoutStringAMDGCN =
34-
"e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32"
34+
"e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32"
3535
"-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-"
3636
"v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-"
3737
"v2048:2048-n32:64-S32-A5-G1-ni:7:8:9";

clang/test/CodeGen/target-data.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,12 @@
160160

161161
// RUN: %clang_cc1 -triple amdgcn-unknown -target-cpu hawaii -o - -emit-llvm %s \
162162
// RUN: | FileCheck %s -check-prefix=R600SI
163-
// R600SI: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
163+
// R600SI: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
164164

165165
// Test default -target-cpu
166166
// RUN: %clang_cc1 -triple amdgcn-unknown -o - -emit-llvm %s \
167167
// RUN: | FileCheck %s -check-prefix=R600SIDefault
168-
// R600SIDefault: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
168+
// R600SIDefault: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
169169

170170
// RUN: %clang_cc1 -triple arm64-unknown -o - -emit-llvm %s | \
171171
// RUN: FileCheck %s -check-prefix=AARCH64
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %clang_cc1 %s -O0 -triple amdgcn -emit-llvm -o - | FileCheck %s
22
// RUN: %clang_cc1 %s -O0 -triple amdgcn---opencl -emit-llvm -o - | FileCheck %s
33

4-
// CHECK: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
4+
// CHECK: target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
55
void foo(void) {}

lld/test/ELF/lto/amdgcn-oses.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
;--- amdhsa.ll
2727
target triple = "amdgcn-amd-amdhsa"
28-
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
28+
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
2929

3030
!llvm.module.flags = !{!0}
3131
!0 = !{i32 1, !"amdhsa_code_object_version", i32 500}
@@ -36,15 +36,15 @@ define void @_start() {
3636

3737
;--- amdpal.ll
3838
target triple = "amdgcn-amd-amdpal"
39-
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
39+
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
4040

4141
define amdgpu_cs void @_start() {
4242
ret void
4343
}
4444

4545
;--- mesa3d.ll
4646
target triple = "amdgcn-amd-mesa3d"
47-
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
47+
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
4848

4949
define void @_start() {
5050
ret void

lld/test/ELF/lto/amdgcn.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
; Make sure the amdgcn triple is handled
66

77
target triple = "amdgcn-amd-amdhsa"
8-
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
8+
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-po3:32:32-p4:64:64-po5:32:32-p6:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5"
99

1010
define void @_start() {
1111
ret void

llvm/docs/LangRef.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3301,8 +3301,12 @@ as follows:
33013301
default address space 0. The value of ``<as>`` must be in the range [1,2^24).
33023302
The optional ``<flags>`` are used to specify properties of pointers in this
33033303
address space: the character ``u`` marks pointers as having an unstable
3304-
representation, and ``e`` marks pointers having external state. See
3305-
:ref:`Non-Integral Pointer Types <nointptrtype>`.
3304+
representation; ``e`` marks pointers having external state; ``z`` marks the
3305+
value of the nullptr as all-zeros (default behavior if it is not specified);
3306+
``o`` marks the value of the nullptr as all-ones; ``c`` marks the value of
3307+
the nullptr as custom (neither all-zeros nor all-ones), such that LLVM will
3308+
not be able to fold various casts involving nullptr.
3309+
See :ref:`Non-Integral Pointer Types <nointptrtype>`.
33063310

33073311
``i<size>:<abi>[:<pref>]``
33083312
This specifies the alignment for an integer type of a given bit

llvm/include/llvm/IR/Constant.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ class Constant : public User {
189189

190190
LLVM_ABI static Constant *getNullValue(Type *Ty);
191191

192+
LLVM_ABI static Constant *getZeroValue(Type *Ty);
193+
192194
/// @returns the value for an integer or vector of integer constant of the
193195
/// given type that has all its bits set to true.
194196
/// Get the all ones value

llvm/include/llvm/IR/DataLayout.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ class DataLayout {
8383
/// for additional metadata (e.g. AMDGPU buffer fat pointers with bounds
8484
/// and other flags or CHERI capabilities that contain bounds+permissions).
8585
uint32_t IndexBitWidth;
86+
/// The value of the nullptr in this address space. It can be three values:
87+
/// all-zeros, all-ones, or std::nullopt. Since we don't have a way to
88+
/// represent an arbitrary bit pattern, we use std::nullopt to represent the
89+
/// case where the nullptr value is neither 0 nor -1.
90+
std::optional<APInt> NullPtrValue;
8691
/// Pointers in this address space don't have a well-defined bitwise
8792
/// representation (e.g. they may be relocated by a copying garbage
8893
/// collector and thus have different addresses at different times).
@@ -158,7 +163,8 @@ class DataLayout {
158163
/// Sets or updates the specification for pointer in the given address space.
159164
void setPointerSpec(uint32_t AddrSpace, uint32_t BitWidth, Align ABIAlign,
160165
Align PrefAlign, uint32_t IndexBitWidth,
161-
bool HasUnstableRepr, bool HasExternalState);
166+
std::optional<APInt> NullPtrValue, bool HasUnstableRepr,
167+
bool HasExternalState);
162168

163169
/// Internal helper to get alignment for integer of given bitwidth.
164170
LLVM_ABI Align getIntegerAlignment(uint32_t BitWidth, bool abi_or_pref) const;
@@ -697,6 +703,11 @@ class DataLayout {
697703
///
698704
/// This includes an explicitly requested alignment (if the global has one).
699705
LLVM_ABI Align getPreferredAlign(const GlobalVariable *GV) const;
706+
707+
/// Returns the value of the nullptr in the given address space.
708+
LLVM_ABI std::optional<APInt> getNullPtrValue(unsigned AddrSpace) const {
709+
return getPointerSpec(AddrSpace).NullPtrValue;
710+
}
700711
};
701712

702713
inline DataLayout *unwrap(LLVMTargetDataRef P) {

llvm/lib/Analysis/ConstantFolding.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,19 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
14971497
llvm_unreachable("Missing case");
14981498
case Instruction::PtrToAddr:
14991499
case Instruction::PtrToInt:
1500+
// If the input is a nullptr, we can fold it to the corresponding nullptr
1501+
// value.
1502+
if (Opcode == Instruction::PtrToInt && C->isNullValue()) {
1503+
if (std::optional<APInt> NullPtrValue = DL.getNullPtrValue(
1504+
C->getType()->getScalarType()->getPointerAddressSpace())) {
1505+
if (NullPtrValue->isZero())
1506+
return Constant::getZeroValue(DestTy);
1507+
else if (NullPtrValue->isAllOnes())
1508+
return Constant::getAllOnesValue(DestTy);
1509+
else
1510+
llvm_unreachable("invalid nullptr value");
1511+
}
1512+
}
15001513
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
15011514
Constant *FoldedValue = nullptr;
15021515
// If the input is an inttoptr, eliminate the pair. This requires knowing
@@ -1543,6 +1556,13 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
15431556
}
15441557
break;
15451558
case Instruction::IntToPtr:
1559+
// We can fold it to a null pointer if the input is the nullptr value.
1560+
if (std::optional<APInt> NullPtrValue = DL.getNullPtrValue(
1561+
DestTy->getScalarType()->getPointerAddressSpace())) {
1562+
if ((NullPtrValue->isZero() && C->isZeroValue()) ||
1563+
(NullPtrValue->isAllOnes() && C->isAllOnesValue()))
1564+
return Constant::getNullValue(DestTy);
1565+
}
15461566
// If the input is a ptrtoint, turn the pair into a ptr to ptr bitcast if
15471567
// the int size is >= the ptr size and the address spaces are the same.
15481568
// This requires knowing the width of a pointer, so it can't be done in
@@ -1561,6 +1581,24 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
15611581
}
15621582
}
15631583
break;
1584+
case Instruction::AddrSpaceCast:
1585+
// A null pointer (`ptr addrspace(N) null` in IR presentation,
1586+
// `ConstantPointerNull` in LLVM class, not `nullptr` in C/C++) used to
1587+
// represent a zero-value pointer in the corresponding address space.
1588+
// Therefore, we can't simply fold an address space cast of a null pointer
1589+
// from one address space to another, because on some targets, the nullptr
1590+
// of an address space could be non-zero.
1591+
//
1592+
// Recently, the semantic of `ptr addrspace(N) null` is changed to represent
1593+
// the actual nullptr in the corresponding address space. It can be zero or
1594+
// non-zero, depending on the target. Therefore, we can fold an address
1595+
// space cast of a nullptr from one address space to another.
1596+
1597+
// If the input is a nullptr, we can fold it to the corresponding
1598+
// nullptr in the destination address space.
1599+
if (C->isNullValue())
1600+
return Constant::getNullValue(DestTy);
1601+
[[fallthrough]];
15641602
case Instruction::Trunc:
15651603
case Instruction::ZExt:
15661604
case Instruction::SExt:
@@ -1570,7 +1608,6 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C,
15701608
case Instruction::SIToFP:
15711609
case Instruction::FPToUI:
15721610
case Instruction::FPToSI:
1573-
case Instruction::AddrSpaceCast:
15741611
break;
15751612
case Instruction::BitCast:
15761613
return FoldBitCast(C, DestTy, DL);

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6575,7 +6575,7 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
65756575
if (auto *TETy = dyn_cast<TargetExtType>(Ty))
65766576
if (!TETy->hasProperty(TargetExtType::HasZeroInit))
65776577
return error(ID.Loc, "invalid type for null constant");
6578-
V = Constant::getNullValue(Ty);
6578+
V = Constant::getZeroValue(Ty);
65796579
return false;
65806580
case ValID::t_None:
65816581
if (!Ty->isTokenTy())

0 commit comments

Comments
 (0)