[llvm] [InferAddressSpaces] Fix null PHI operand when an incoming flat expression is not rewritten (PR #205749)
Fujun Han via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 30 16:48:14 PDT 2026
https://github.com/Peter9606 updated https://github.com/llvm/llvm-project/pull/205749
>From 8af6bf3b502221bd47de79356c1c4392eed7e11b Mon Sep 17 00:00:00 2001
From: Fujun Han <fujun.han at iluvatar.com>
Date: Thu, 25 Jun 2026 16:36:54 +0800
Subject: [PATCH] [InferAddressSpaces] Fix null PHI operand when an incoming
flat expression is not rewritten
When inferring a PHI into a non-flat address space, an unrewritten flat
incoming instruction is replaced by a poison placeholder during cloning.
If the fixup loop cannot find a rewritten value, insert an addrspacecast
for the operand instead of storing a null incoming value.
Assisted-by: Cursor (claude-opus-4-8-thinking-high)
Signed-off-by: Fujun Han <fujun.han at iluvatar.com>
---
.../Transforms/Scalar/InferAddressSpaces.cpp | 39 ++++--
.../AMDGPU/phi-null-operand.ll | 132 ++++++++++++++++++
2 files changed, 160 insertions(+), 11 deletions(-)
create mode 100644 llvm/test/Transforms/InferAddressSpaces/AMDGPU/phi-null-operand.ll
diff --git a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
index d11fa0e5c9ce0..754083dcd2834 100644
--- a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
@@ -766,6 +766,20 @@ static Value *phiNodeOperandWithNewAddressSpace(AddrSpaceCastInst *NewI,
return NewI;
}
+// Inserts an addrspacecast for an instruction operand, placing the cast so
+// that it dominates the user. For PHI users, delegates to
+// phiNodeOperandWithNewAddressSpace.
+static Value *insertAddrSpaceCastForUserOperand(Value *Operand, Type *NewPtrTy,
+ Instruction *User) {
+ auto *ASC = new AddrSpaceCastInst(Operand, NewPtrTy);
+ if (LLVM_UNLIKELY(User->getOpcode() == Instruction::PHI))
+ return phiNodeOperandWithNewAddressSpace(ASC, Operand);
+
+ ASC->insertBefore(User->getIterator());
+ ASC->setDebugLoc(User->getDebugLoc());
+ return ASC;
+}
+
// A helper function for cloneInstructionWithNewAddressSpace. Returns the clone
// of OperandUse.get() in the new address space. If the clone is not ready yet,
// returns poison in the new address space as a placeholder.
@@ -790,14 +804,7 @@ static Value *operandWithNewAddressSpaceOrCreatePoison(
// Insert an addrspacecast on that operand before the user.
unsigned NewAS = I->second;
Type *NewPtrTy = getPtrOrVecOfPtrsWithNewAS(Operand->getType(), NewAS);
- auto *NewI = new AddrSpaceCastInst(Operand, NewPtrTy);
-
- if (LLVM_UNLIKELY(Inst->getOpcode() == Instruction::PHI))
- return phiNodeOperandWithNewAddressSpace(NewI, Operand);
-
- NewI->insertBefore(Inst->getIterator());
- NewI->setDebugLoc(Inst->getDebugLoc());
- return NewI;
+ return insertAddrSpaceCastForUserOperand(Operand, NewPtrTy, Inst);
}
PoisonUsesToFix->push_back(&OperandUse);
@@ -1549,9 +1556,19 @@ bool InferAddressSpacesImpl::rewriteWithNewAddressSpaces(
unsigned OperandNo = PoisonUse->getOperandNo();
assert(isa<PoisonValue>(NewV->getOperand(OperandNo)));
- WeakTrackingVH NewOp = ValueWithNewAddrSpace.lookup(PoisonUse->get());
- assert(NewOp &&
- "poison replacements in ValueWithNewAddrSpace shouldn't be null");
+ Value *NewOp = ValueWithNewAddrSpace.lookup(PoisonUse->get());
+ if (!NewOp) {
+ // The operand was never rewritten into the new address space (e.g. a flat
+ // address expression in a flat-only cycle that stays in the
+ // uninitialized address space). Only instructions reach here;
+ // operandWithNewAddressSpaceOrCreatePoison() handles Constant operands
+ // earlier. Materialize an explicit addrspacecast, mirroring the
+ // PredicatedAS path in that function.
+ Value *Orig = PoisonUse->get();
+ Type *NewPtrTy = NewV->getOperand(OperandNo)->getType();
+ NewOp = insertAddrSpaceCastForUserOperand(Orig, NewPtrTy,
+ cast<Instruction>(NewV));
+ }
NewV->setOperand(OperandNo, NewOp);
}
diff --git a/llvm/test/Transforms/InferAddressSpaces/AMDGPU/phi-null-operand.ll b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/phi-null-operand.ll
new file mode 100644
index 0000000000000..c36da4e9cd542
--- /dev/null
+++ b/llvm/test/Transforms/InferAddressSpaces/AMDGPU/phi-null-operand.ll
@@ -0,0 +1,132 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -S -passes=infer-address-spaces %s | FileCheck %s
+
+ at g = external global i8, align 1
+
+; Each function infers merge PHI %p into addrspace(1) because %arg.cast
+; represents an addrspace(1) pointer. The other incoming value %xn is a flat
+; getelementptr in a flat-only cycle whose pointer operand is seeded with a
+; different flat address (poison, null, a global, or a constant expression).
+; %xn stays in the uninitialized address space and is never rewritten, so
+; cloning the PHI records a poison placeholder and the fixup loop materializes
+; an addrspacecast on the %loop -> %merge incoming edge.
+
+; Incoming instruction (%xn): flat-only cycle seeded by poison.
+define void @phi_incoming_instruction(ptr addrspace(1) %arg, i1 %c) {
+; CHECK-LABEL: @phi_incoming_instruction(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[MERGE:%.*]], label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[X:%.*]] = phi ptr [ poison, [[ENTRY:%.*]] ], [ [[XN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[XN]] = getelementptr i8, ptr [[X]], i64 1
+; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr [[XN]] to ptr addrspace(1)
+; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[P:%.*]] = phi ptr addrspace(1) [ [[ARG:%.*]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %arg.cast = addrspacecast ptr addrspace(1) %arg to ptr
+ br i1 %c, label %merge, label %loop
+
+loop:
+ %x = phi ptr [ poison, %entry ], [ %xn, %loop ]
+ %xn = getelementptr i8, ptr %x, i64 1
+ br i1 %c, label %loop, label %merge
+
+merge:
+ %p = phi ptr [ %arg.cast, %entry ], [ %xn, %loop ]
+ %v = load i32, ptr %p
+ ret void
+}
+
+; Incoming instruction (%xn): flat-only cycle seeded by null.
+define void @phi_incoming_null(ptr addrspace(1) %arg, i1 %c) {
+; CHECK-LABEL: @phi_incoming_null(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[MERGE:%.*]], label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[X:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[XN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[XN]] = getelementptr i8, ptr [[X]], i64 1
+; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr [[XN]] to ptr addrspace(1)
+; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[P:%.*]] = phi ptr addrspace(1) [ [[ARG:%.*]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %arg.cast = addrspacecast ptr addrspace(1) %arg to ptr
+ br i1 %c, label %merge, label %loop
+
+loop:
+ %x = phi ptr [ null, %entry ], [ %xn, %loop ]
+ %xn = getelementptr i8, ptr %x, i64 1
+ br i1 %c, label %loop, label %merge
+
+merge:
+ %p = phi ptr [ %arg.cast, %entry ], [ %xn, %loop ]
+ %v = load i32, ptr %p
+ ret void
+}
+
+; Incoming instruction (%xn): flat-only cycle seeded by a global address.
+define void @phi_incoming_global(ptr addrspace(1) %arg, i1 %c) {
+; CHECK-LABEL: @phi_incoming_global(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[MERGE:%.*]], label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[X:%.*]] = phi ptr [ @g, [[ENTRY:%.*]] ], [ [[XN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[XN]] = getelementptr i8, ptr [[X]], i64 1
+; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr [[XN]] to ptr addrspace(1)
+; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[P:%.*]] = phi ptr addrspace(1) [ [[ARG:%.*]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ]
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr addrspace(1) [[P]], align 1
+; CHECK-NEXT: ret void
+;
+entry:
+ %arg.cast = addrspacecast ptr addrspace(1) %arg to ptr
+ br i1 %c, label %merge, label %loop
+
+loop:
+ %x = phi ptr [ @g, %entry ], [ %xn, %loop ]
+ %xn = getelementptr i8, ptr %x, i64 1
+ br i1 %c, label %loop, label %merge
+
+merge:
+ %p = phi ptr [ %arg.cast, %entry ], [ %xn, %loop ]
+ %v = load i8, ptr %p
+ ret void
+}
+
+; Incoming instruction (%xn): flat-only cycle seeded by a constant expr.
+define void @phi_incoming_constant_expr(ptr addrspace(1) %arg, i1 %c) {
+; CHECK-LABEL: @phi_incoming_constant_expr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[MERGE:%.*]], label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[X:%.*]] = phi ptr [ inttoptr (i64 4096 to ptr), [[ENTRY:%.*]] ], [ [[XN:%.*]], [[LOOP]] ]
+; CHECK-NEXT: [[XN]] = getelementptr i8, ptr [[X]], i64 1
+; CHECK-NEXT: [[TMP0:%.*]] = addrspacecast ptr [[XN]] to ptr addrspace(1)
+; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[MERGE]]
+; CHECK: merge:
+; CHECK-NEXT: [[P:%.*]] = phi ptr addrspace(1) [ [[ARG:%.*]], [[ENTRY]] ], [ [[TMP0]], [[LOOP]] ]
+; CHECK-NEXT: [[V:%.*]] = load i32, ptr addrspace(1) [[P]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %arg.cast = addrspacecast ptr addrspace(1) %arg to ptr
+ br i1 %c, label %merge, label %loop
+
+loop:
+ %x = phi ptr [ inttoptr (i64 4096 to ptr), %entry ], [ %xn, %loop ]
+ %xn = getelementptr i8, ptr %x, i64 1
+ br i1 %c, label %loop, label %merge
+
+merge:
+ %p = phi ptr [ %arg.cast, %entry ], [ %xn, %loop ]
+ %v = load i32, ptr %p
+ ret void
+}
More information about the llvm-commits
mailing list