[llvm] [InferAddressSpaces] Fix null PHI operand when an incoming flat expression is not rewritten (PR #205749)
Pankaj Dwivedi via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 23:56:05 PDT 2026
================
@@ -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
----------------
PankajDwivedi-25 wrote:
I think the tests here are encoding the wrong expectation. In **phi_incoming_constant_expr** the cycle starts from inttoptr (i64 4096 to ptr), which is just some flat address. we never establish that it points into global
memory. But the CHECK lines expect us to cast it to addrspace(1) and load from it as global. That cast is poison unless the address really is in the global aperture, so the load becomes UB, while the flat load in the input was fine. **phi_incoming_global** reads the same way to me with @g. So unless I'm missing
something, these two tests aren't just checking that we stopped crashing, they're baking a miscompile in as the expected output.
https://github.com/llvm/llvm-project/pull/205749
More information about the llvm-commits
mailing list