[llvm-branch-commits] [llvm] InstSimplify: Fold nonnull addrspacecast of null to poison (PR #217905)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Aug 21 05:31:44 PDT 2026


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/217905

Perform the definitional fold if the source value is null
in the default address space.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>

>From 9aa97a0cd87c13749d4a63fbde7a0a6ab839521f Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 21 Aug 2026 12:26:48 +0200
Subject: [PATCH] InstSimplify: Fold nonnull addrspacecast of null to poison

Perform the definitional fold if the source value is null
in the default address space.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
 .../llvm/Analysis/InstructionSimplify.h       |  4 ++
 llvm/lib/Analysis/InstructionSimplify.cpp     | 22 ++++++
 .../InstSimplify/addrspacecast-nonnull.ll     | 69 +++++++++++++++++++
 3 files changed, 95 insertions(+)
 create mode 100644 llvm/test/Transforms/InstSimplify/addrspacecast-nonnull.ll

diff --git a/llvm/include/llvm/Analysis/InstructionSimplify.h b/llvm/include/llvm/Analysis/InstructionSimplify.h
index 8f5a484ad2d57..61e1c59b59f56 100644
--- a/llvm/include/llvm/Analysis/InstructionSimplify.h
+++ b/llvm/include/llvm/Analysis/InstructionSimplify.h
@@ -194,6 +194,10 @@ LLVM_ABI Value *simplifyExtractElementInst(Value *Vec, Value *Idx,
 LLVM_ABI Value *simplifyCastInst(unsigned CastOpc, Value *Op, Type *Ty,
                                  const SimplifyQuery &Q);
 
+/// Given operands for an AddrSpaceCastInst, fold the result or return null.
+LLVM_ABI Value *simplifyAddrSpaceCastInst(Value *Op, Type *Ty, bool IsNonNull,
+                                          const SimplifyQuery &Q);
+
 /// Given operands for an intrinsic, fold the result or return null. Context
 /// Function is passed as \p CxtF. \p ExBehavior and \p Rounding only apply to
 /// constrained FP intrinsics.
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c76f4d2e9f327..b662b6921319d 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -5707,6 +5707,22 @@ Value *llvm::simplifyCastInst(unsigned CastOpc, Value *Op, Type *Ty,
   return ::simplifyCastInst(CastOpc, Op, Ty, Q, RecursionLimit);
 }
 
+static Value *simplifyAddrSpaceCastInst(Value *Op, Type *Ty, bool IsNonNull,
+                                        const SimplifyQuery &Q,
+                                        unsigned MaxRecurse) {
+  if (IsNonNull && match(Op, m_Zero()) && Q.CxtI &&
+      !NullPointerIsDefined(Q.CxtI->getFunction(),
+                            Op->getType()->getPointerAddressSpace()))
+    return PoisonValue::get(Ty);
+
+  return ::simplifyCastInst(Instruction::AddrSpaceCast, Op, Ty, Q, MaxRecurse);
+}
+
+Value *llvm::simplifyAddrSpaceCastInst(Value *Op, Type *Ty, bool IsNonNull,
+                                       const SimplifyQuery &Q) {
+  return ::simplifyAddrSpaceCastInst(Op, Ty, IsNonNull, Q, RecursionLimit);
+}
+
 /// For the given destination element of a shuffle, peek through shuffles to
 /// match a root vector source operand that contains that element in the same
 /// vector lane (ie, the same mask index), so we can eliminate the shuffle(s).
@@ -7831,6 +7847,12 @@ static Value *simplifyInstructionWithOperands(Instruction *I,
 #define HANDLE_CAST_INST(num, opc, clas) case Instruction::opc:
 #include "llvm/IR/Instruction.def"
 #undef HANDLE_CAST_INST
+    if (I->getOpcode() == Instruction::AddrSpaceCast) {
+      return simplifyAddrSpaceCastInst(NewOps[0], I->getType(),
+                                       cast<AddrSpaceCastInst>(I)->hasNonNull(),
+                                       Q, MaxRecurse);
+    }
+
     return simplifyCastInst(I->getOpcode(), NewOps[0], I->getType(), Q,
                             MaxRecurse);
   case Instruction::Alloca:
diff --git a/llvm/test/Transforms/InstSimplify/addrspacecast-nonnull.ll b/llvm/test/Transforms/InstSimplify/addrspacecast-nonnull.ll
new file mode 100644
index 0000000000000..819577a802c4f
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/addrspacecast-nonnull.ll
@@ -0,0 +1,69 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
+
+; A nonnull addrspacecast of the address space 0 null value is poison.
+define ptr addrspace(1) @fold_scalar() {
+; CHECK-LABEL: @fold_scalar(
+; CHECK-NEXT:    ret ptr addrspace(1) poison
+;
+  %c = addrspacecast nonnull ptr null to ptr addrspace(1)
+  ret ptr addrspace(1) %c
+}
+
+; A splat null vector of address space 0 pointers folds elementwise.
+define <2 x ptr addrspace(1)> @fold_vector() {
+; CHECK-LABEL: @fold_vector(
+; CHECK-NEXT:    ret <2 x ptr addrspace(1)> poison
+;
+  %c = addrspacecast nonnull <2 x ptr> zeroinitializer to <2 x ptr addrspace(1)>
+  ret <2 x ptr addrspace(1)> %c
+}
+
+define <2 x ptr addrspace(1)> @no_fold_splat_poison() {
+; CHECK-LABEL: @no_fold_splat_poison(
+; CHECK-NEXT:    ret <2 x ptr addrspace(1)> <ptr addrspace(1) addrspacecast (ptr null to ptr addrspace(1)), ptr addrspace(1) poison>
+;
+  %c = addrspacecast nonnull <2 x ptr> <ptr null, ptr poison> to <2 x ptr addrspace(1)>
+  ret <2 x ptr addrspace(1)> %c
+}
+
+; Without the nonnull flag the cast is not poison.
+define ptr addrspace(1) @no_fold_without_flag() {
+; CHECK-LABEL: @no_fold_without_flag(
+; CHECK-NEXT:    ret ptr addrspace(1) addrspacecast (ptr null to ptr addrspace(1))
+;
+  %c = addrspacecast ptr null to ptr addrspace(1)
+  ret ptr addrspace(1) %c
+}
+
+; A non-null source is not folded.
+define ptr addrspace(1) @no_fold_nonzero_src(ptr %p) {
+; CHECK-LABEL: @no_fold_nonzero_src(
+; CHECK-NEXT:    [[C:%.*]] = addrspacecast nonnull ptr [[P:%.*]] to ptr addrspace(1)
+; CHECK-NEXT:    ret ptr addrspace(1) [[C]]
+;
+  %c = addrspacecast nonnull ptr %p to ptr addrspace(1)
+  ret ptr addrspace(1) %c
+}
+
+; The null value of a non-zero source address space is not necessarily the zero
+; pointer, so casting it is not folded to poison.
+define ptr @no_fold_src_as1() {
+; CHECK-LABEL: @no_fold_src_as1(
+; CHECK-NEXT:    ret ptr addrspacecast (ptr addrspace(1) null to ptr)
+;
+  %c = addrspacecast nonnull ptr addrspace(1) null to ptr
+  ret ptr %c
+}
+
+; When null is a valid address for the function, the source may legitimately be
+; the null pointer, so the fold does not apply.
+define ptr addrspace(1) @no_fold_null_valid() #0 {
+; CHECK-LABEL: @no_fold_null_valid(
+; CHECK-NEXT:    ret ptr addrspace(1) addrspacecast (ptr null to ptr addrspace(1))
+;
+  %c = addrspacecast nonnull ptr null to ptr addrspace(1)
+  ret ptr addrspace(1) %c
+}
+
+attributes #0 = { null_pointer_is_valid }



More information about the llvm-branch-commits mailing list