[llvm] IR: Add nonnull flag to addrspacecast (PR #217903)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 10:02:05 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
Introduce the nonnull flag on the addrspacecast instruction,
asserting that the source pointer is not the null value of its
source address space. If the source is the source-address-space null
value, the result is poison.
The LangRef does not yet acknowdlege the existence of non-0 null
pointers, or null pointers for address spaces other than default,
but will "soon".
This gives the target a way to omit the runtime null check to map
between the null values. Currently AMDGPU works around the lack of
this flag by introducing the hacky llvm.amdgcn.addrspacecast.nonnull
intrinsic in a late target codegen pass.
Co-authored-by: Claude (Claude-Opus-4.8) <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/217903.diff
13 Files Affected:
- (modified) llvm/docs/LangRef.md (+6-1)
- (modified) llvm/include/llvm/Bitcode/LLVMBitCodes.h (+3)
- (modified) llvm/include/llvm/IR/IRBuilder.h (+10-3)
- (modified) llvm/include/llvm/IR/Instructions.h (+10)
- (modified) llvm/lib/AsmParser/LLParser.cpp (+8-1)
- (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+3)
- (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+3)
- (modified) llvm/lib/IR/AsmWriter.cpp (+3)
- (modified) llvm/test/Assembler/flags.ll (+12)
- (modified) llvm/test/Bitcode/compatibility.ll (+3-1)
- (modified) llvm/test/Bitcode/flags.ll (+5-1)
- (modified) llvm/test/Transforms/InferAddressSpaces/AMDGPU/infer-addrspacecast.ll (+19)
- (modified) llvm/test/Transforms/InstCombine/addrspacecast.ll (+11)
``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 82eebd26c0054..90b1dd94e9e1c 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -13263,7 +13263,8 @@ If `value` is of the {ref}`byte type <t_byte>`:
##### Syntax:
```
-<result> = addrspacecast <pty> <ptrval> to <pty2> ; yields pty2
+<result> = addrspacecast <pty> <ptrval> to <pty2> ; yields pty2
+<result> = addrspacecast nonnull <pty> <ptrval> to <pty2> ; yields pty2
```
##### Overview:
@@ -13300,6 +13301,10 @@ should yield the original bit pattern).
Which address space casts are supported depends on the target. Unsupported
address space casts return {ref}`poison <poisonvalues>`.
+The optional `nonnull` flag asserts that `ptrval` is not the null value of
+its source address space; if it is, the result is
+{ref}`poison <poisonvalues>`.
+
##### Example:
```llvm
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 226be19624db0..8771ab0103604 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -572,6 +572,9 @@ enum PossiblyExactOperatorOptionalFlags { PEO_EXACT = 0 };
/// PossiblyDisjointInst's SubclassOptionalData contents.
enum PossiblyDisjointInstOptionalFlags { PDI_DISJOINT = 0 };
+/// Flags for serializing AddrSpaceCastInst's SubclassOptionalData contents.
+enum AddrSpaceCastInstOptionalFlags { ASCI_NON_NULL = 0 };
+
/// Mark to distinguish metadata from value in an operator bundle.
enum MetadataOperandBundleValueMarker { OB_METADATA = 0x80000000 };
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index f2621bbe298df..ddf6740b363d7 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -2245,9 +2245,16 @@ class IRBuilderBase {
return CreateCast(Instruction::BitCast, V, DestTy, Name);
}
- Value *CreateAddrSpaceCast(Value *V, Type *DestTy,
- const Twine &Name = "") {
- return CreateCast(Instruction::AddrSpaceCast, V, DestTy, Name);
+ Value *CreateAddrSpaceCast(Value *V, Type *DestTy, const Twine &Name = "",
+ bool IsNonNull = false) {
+ if (V->getType() == DestTy)
+ return V;
+ if (Value *Folded = Folder.FoldCast(Instruction::AddrSpaceCast, V, DestTy))
+ return Folded;
+ Instruction *I = Insert(new AddrSpaceCastInst(V, DestTy), Name);
+ if (IsNonNull)
+ cast<AddrSpaceCastInst>(I)->setNonNull();
+ return I;
}
Value *CreateZExtOrBitCast(Value *V, Type *DestTy, const Twine &Name = "") {
diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h
index 0475798b9463d..ae874d78aba47 100644
--- a/llvm/include/llvm/IR/Instructions.h
+++ b/llvm/include/llvm/IR/Instructions.h
@@ -5190,6 +5190,8 @@ class AddrSpaceCastInst : public CastInst {
LLVM_ABI AddrSpaceCastInst *cloneImpl() const;
public:
+ enum { NonNull = (1 << 0) };
+
/// Constructor with insert-before-instruction semantics
LLVM_ABI AddrSpaceCastInst(
Value *S, ///< The value to be casted
@@ -5207,6 +5209,14 @@ class AddrSpaceCastInst : public CastInst {
return isa<Instruction>(V) && classof(cast<Instruction>(V));
}
+ void setNonNull(bool B = true) {
+ SubclassOptionalData = (SubclassOptionalData & ~NonNull) | (B * NonNull);
+ }
+
+ /// Test whether the source is known not to be the null value of its
+ /// address space.
+ bool hasNonNull() const { return (SubclassOptionalData & NonNull) != 0; }
+
/// Gets the pointer operand.
Value *getPointerOperand() {
return getOperand(0);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 93a79a7035e6f..2ecd4a686f1cf 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -7769,9 +7769,16 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB,
cast<TruncInst>(Inst)->setHasNoSignedWrap(true);
return false;
}
+ case lltok::kw_addrspacecast: {
+ bool NonNull = EatIfPresent(lltok::kw_nonnull);
+ if (parseCast(Inst, PFS, KeywordVal))
+ return true;
+ if (NonNull)
+ cast<AddrSpaceCastInst>(Inst)->setNonNull();
+ return false;
+ }
case lltok::kw_sext:
case lltok::kw_bitcast:
- case lltok::kw_addrspacecast:
case lltok::kw_fptoui:
case lltok::kw_fptosi:
case lltok::kw_inttoptr:
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index bf8f6d12ce5a8..1d7fbd3d8b0ff 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -5368,6 +5368,9 @@ Error BitcodeReader::parseFunctionBody(Function *F) {
cast<TruncInst>(I)->setHasNoUnsignedWrap(true);
if (Record[OpNum] & (1 << bitc::TIO_NO_SIGNED_WRAP))
cast<TruncInst>(I)->setHasNoSignedWrap(true);
+ } else if (Opc == Instruction::AddrSpaceCast) {
+ if (Record[OpNum] & (1 << bitc::ASCI_NON_NULL))
+ cast<AddrSpaceCastInst>(I)->setNonNull(true);
}
if (isa<FPMathOperator>(I)) {
uint64_t Flags = Record[OpNum];
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 3b4065c0c7895..0ab5495a90e4d 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1857,6 +1857,9 @@ static uint64_t getOptimizationFlags(const Value *V) {
} else if (const auto *ICmp = dyn_cast<ICmpInst>(V)) {
if (ICmp->hasSameSign())
Flags |= 1 << bitc::ICMP_SAME_SIGN;
+ } else if (const auto *ASC = dyn_cast<AddrSpaceCastInst>(V)) {
+ if (ASC->hasNonNull())
+ Flags |= 1 << bitc::ASCI_NON_NULL;
}
return Flags;
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index c3202eea12c28..346246a66e2df 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -1534,6 +1534,9 @@ static void writeOptimizationInfo(raw_ostream &Out, const User *U) {
} else if (const auto *ICmp = dyn_cast<ICmpInst>(U)) {
if (ICmp->hasSameSign())
Out << " samesign";
+ } else if (const auto *ASC = dyn_cast<AddrSpaceCastInst>(U)) {
+ if (ASC->hasNonNull())
+ Out << " nonnull";
}
}
diff --git a/llvm/test/Assembler/flags.ll b/llvm/test/Assembler/flags.ll
index b685277f4ee04..45011a06b4b1c 100644
--- a/llvm/test/Assembler/flags.ll
+++ b/llvm/test/Assembler/flags.ll
@@ -293,6 +293,18 @@ define i1 @test_icmp_samesign(i32 %a, i32 %b) {
ret i1 %res
}
+define ptr @test_addrspacecast_nonnull(ptr addrspace(1) %a) {
+; CHECK: %res = addrspacecast nonnull ptr addrspace(1) %a to ptr
+ %res = addrspacecast nonnull ptr addrspace(1) %a to ptr
+ ret ptr %res
+}
+
+define <2 x ptr> @test_addrspacecast_nonnull_vector(<2 x ptr addrspace(1)> %a) {
+; CHECK: %res = addrspacecast nonnull <2 x ptr addrspace(1)> %a to <2 x ptr>
+ %res = addrspacecast nonnull <2 x ptr addrspace(1)> %a to <2 x ptr>
+ ret <2 x ptr> %res
+}
+
define <2 x i1> @test_icmp_samesign2(<2 x i32> %a, <2 x i32> %b) {
; CHECK: %res = icmp samesign ult <2 x i32> %a, %b
%res = icmp samesign ult <2 x i32> %a, %b
diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index 768fa6d6ec735..d8784b4d8a2f2 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -1712,7 +1712,7 @@ define void @instructions.memops(ptr %base) {
}
; Instructions -- Conversion Operations
-define void @instructions.conversions() {
+define void @instructions.conversions(ptr %pop) {
trunc i32 -1 to i1
; CHECK: trunc i32 -1 to i1
zext i32 -1 to i64
@@ -1747,6 +1747,8 @@ define void @instructions.conversions() {
; CHECK: bitcast i32 0 to i32
addrspacecast ptr null to ptr addrspace(1)
; CHECK: addrspacecast ptr null to ptr addrspace(1)
+ addrspacecast nonnull ptr %pop to ptr addrspace(1)
+ ; CHECK: addrspacecast nonnull ptr %pop to ptr addrspace(1)
ret void
}
diff --git a/llvm/test/Bitcode/flags.ll b/llvm/test/Bitcode/flags.ll
index 99988c9ba3d3d..b20f0566bddef 100644
--- a/llvm/test/Bitcode/flags.ll
+++ b/llvm/test/Bitcode/flags.ll
@@ -7,7 +7,7 @@
; Make sure the flags are serialized/deserialized properly for both
; forward and backward references.
-define void @foo() nounwind {
+define void @foo(ptr addrspace(1) %p, <2 x ptr addrspace(1)> %pv) nounwind {
entry:
br label %first
@@ -32,6 +32,9 @@ second: ; preds = %first
%tv = trunc <2 x i32> %aa to <2 x i16>
%ii = icmp samesign ult i32 %a, %z
%iv = icmp samesign ult <2 x i32> %aa, %aa
+ %an = addrspacecast nonnull ptr addrspace(1) %p to ptr
+ %ap = addrspacecast ptr addrspace(1) %p to ptr
+ %anv = addrspacecast nonnull <2 x ptr addrspace(1)> %pv to <2 x ptr>
unreachable
first: ; preds = %entry
@@ -57,5 +60,6 @@ first: ; preds = %entry
%ttv = trunc <2 x i32> %aa to <2 x i16>
%icm = icmp samesign ult i32 %a, %zz
%icv = icmp samesign ult <2 x i32> %aa, %aa
+ %ann = addrspacecast nonnull ptr addrspace(1) %p to ptr
br label %second
}
diff --git a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/infer-addrspacecast.ll b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/infer-addrspacecast.ll
index 4375eebd8e16b..b723d171bada3 100644
--- a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/infer-addrspacecast.ll
+++ b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/infer-addrspacecast.ll
@@ -53,3 +53,22 @@ define void @multiuse_addrspacecast_gep_addrspacecast(ptr addrspace(3) %ptr) {
store i32 8, ptr addrspace(3) %asc1, align 8
ret void
}
+
+; nonnull is dropped when the cast is rebuilt on the sunk select operand.
+; CHECK-LABEL: @rebuilt_addrspacecast_drops_nonnull(
+; CHECK: %sel = select i1 %c, ptr addrspace(3) %p, ptr addrspace(3) %q
+; CHECK-NEXT: %1 = addrspacecast ptr addrspace(3) %sel to ptr
+; CHECK-NOT: nonnull
+; CHECK-NEXT: call void @use_flat(ptr %1)
+; CHECK-NEXT: %v = load i32, ptr addrspace(3) %sel, align 4
+; CHECK-NEXT: ret i32 %v
+define i32 @rebuilt_addrspacecast_drops_nonnull(i1 %c, ptr addrspace(3) %p, ptr addrspace(3) %q) {
+ %pf = addrspacecast nonnull ptr addrspace(3) %p to ptr
+ %qf = addrspacecast nonnull ptr addrspace(3) %q to ptr
+ %sel = select i1 %c, ptr %pf, ptr %qf
+ call void @use_flat(ptr %sel)
+ %v = load i32, ptr %sel
+ ret i32 %v
+}
+
+declare void @use_flat(ptr)
diff --git a/llvm/test/Transforms/InstCombine/addrspacecast.ll b/llvm/test/Transforms/InstCombine/addrspacecast.ll
index ff37149bdab7b..3761a91ca87c8 100644
--- a/llvm/test/Transforms/InstCombine/addrspacecast.ll
+++ b/llvm/test/Transforms/InstCombine/addrspacecast.ll
@@ -241,3 +241,14 @@ define void @constant_fold_gep_inttoptr() #0 {
store i32 7, ptr addrspace(4) %cast
ret void
}
+
+; nonnull is dropped when a cast-of-cast pair collapses to a single cast.
+define ptr addrspace(1) @drop_nonnull_on_collapsed_castcast(ptr addrspace(3) %p) {
+; CHECK-LABEL: @drop_nonnull_on_collapsed_castcast(
+; CHECK-NEXT: [[B:%.*]] = addrspacecast ptr addrspace(3) [[P:%.*]] to ptr addrspace(1)
+; CHECK-NEXT: ret ptr addrspace(1) [[B]]
+;
+ %a = addrspacecast ptr addrspace(3) %p to ptr
+ %b = addrspacecast nonnull ptr %a to ptr addrspace(1)
+ ret ptr addrspace(1) %b
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/217903
More information about the llvm-commits
mailing list