[llvm] [SCEV,GVN] Add additional ptrtoaddr tests (NFC) (PR #209049)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Sun Jul 12 12:56:42 PDT 2026
https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/209049
Extend test coverage with files with mixied ptrtoaddr/ptrtoint, as well as wide pointers.
Extra test coverage for
https://github.com/llvm/llvm-project/pull/180244.
>From 242051e1a7d92e02233c92fbf0cb24000e250869 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 9 Jun 2026 20:46:26 +0200
Subject: [PATCH] [SCEV,GVN] Add additional ptrtoaddr tests (NFC)
Extend test coverage with files with mixied ptrtoaddr/ptrtoint, as well
as wide pointers.
Extra test coverage for
https://github.com/llvm/llvm-project/pull/180244.
---
.../ptrtoint-special-pointers.ll | 194 ++++++++++++++++++
llvm/test/Transforms/GVN/ptrtoaddr.ll | 46 +++++
.../expand-ptrtoaddr-reuse-ptrtoint.ll | 46 +++++
3 files changed, 286 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolution/ptrtoint-special-pointers.ll
create mode 100644 llvm/test/Transforms/IndVarSimplify/AArch64/expand-ptrtoaddr-reuse-ptrtoint.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/ptrtoint-special-pointers.ll b/llvm/test/Analysis/ScalarEvolution/ptrtoint-special-pointers.ll
new file mode 100644
index 0000000000000..b9877f46189d8
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/ptrtoint-special-pointers.ll
@@ -0,0 +1,194 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt -passes='print<scalar-evolution>' -disable-output %s 2>&1 | FileCheck %s
+
+target datalayout = "e-p:64:64:64-p1:128:128:128:64-pu2:64:64:64:64-pe3:64:64:64:64"
+
+; Address space 1: integral fat pointer, 128-bit representation, 64-bit address.
+; Address space 2: unstable representation (must stay SCEVUnknown).
+; Address space 3: external state but stable representation.
+
+; The difference of two ptrtoints of a 128-bit fat pointer to i64 (the address
+; width) equals the difference of the addresses, so it can be modeled via
+; ptrtoaddr.
+define i64 @sub_ptrtoint_fat_to_addrwidth(ptr addrspace(1) %p, ptr addrspace(1) %q) {
+; CHECK-LABEL: 'sub_ptrtoint_fat_to_addrwidth'
+; CHECK-NEXT: Classifying expressions for: @sub_ptrtoint_fat_to_addrwidth
+; CHECK-NEXT: %p.int = ptrtoint ptr addrspace(1) %p to i64
+; CHECK-NEXT: --> %p.int U: full-set S: full-set
+; CHECK-NEXT: %q.int = ptrtoint ptr addrspace(1) %q to i64
+; CHECK-NEXT: --> %q.int U: full-set S: full-set
+; CHECK-NEXT: %sub = sub i64 %p.int, %q.int
+; CHECK-NEXT: --> ((-1 * (ptrtoaddr ptr addrspace(1) %q to i64)) + (ptrtoaddr ptr addrspace(1) %p to i64)) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @sub_ptrtoint_fat_to_addrwidth
+;
+ %p.int = ptrtoint ptr addrspace(1) %p to i64
+ %q.int = ptrtoint ptr addrspace(1) %q to i64
+ %sub = sub i64 %p.int, %q.int
+ ret i64 %sub
+}
+
+; ptrtoint to i128 (wider than the 64-bit address) cannot be modeled via
+; ptrtoaddr without losing the high bits, so it must stay opaque.
+define i128 @sub_ptrtoint_fat_wider_than_addr(ptr addrspace(1) %p, ptr addrspace(1) %q) {
+; CHECK-LABEL: 'sub_ptrtoint_fat_wider_than_addr'
+; CHECK-NEXT: Classifying expressions for: @sub_ptrtoint_fat_wider_than_addr
+; CHECK-NEXT: %p.int = ptrtoint ptr addrspace(1) %p to i128
+; CHECK-NEXT: --> %p.int U: full-set S: full-set
+; CHECK-NEXT: %q.int = ptrtoint ptr addrspace(1) %q to i128
+; CHECK-NEXT: --> %q.int U: full-set S: full-set
+; CHECK-NEXT: %sub = sub i128 %p.int, %q.int
+; CHECK-NEXT: --> ((-1 * %q.int) + %p.int) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @sub_ptrtoint_fat_wider_than_addr
+;
+ %p.int = ptrtoint ptr addrspace(1) %p to i128
+ %q.int = ptrtoint ptr addrspace(1) %q to i128
+ %sub = sub i128 %p.int, %q.int
+ ret i128 %sub
+}
+
+; Unstable representation: ptrtoaddr is not valid, so the difference must stay
+; opaque (SCEVUnknown for each ptrtoint).
+define i64 @sub_ptrtoint_unstable(ptr addrspace(2) %p, ptr addrspace(2) %q) {
+; CHECK-LABEL: 'sub_ptrtoint_unstable'
+; CHECK-NEXT: Classifying expressions for: @sub_ptrtoint_unstable
+; CHECK-NEXT: %p.int = ptrtoint ptr addrspace(2) %p to i64
+; CHECK-NEXT: --> %p.int U: full-set S: full-set
+; CHECK-NEXT: %q.int = ptrtoint ptr addrspace(2) %q to i64
+; CHECK-NEXT: --> %q.int U: full-set S: full-set
+; CHECK-NEXT: %sub = sub i64 %p.int, %q.int
+; CHECK-NEXT: --> ((-1 * %q.int) + %p.int) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @sub_ptrtoint_unstable
+;
+ %p.int = ptrtoint ptr addrspace(2) %p to i64
+ %q.int = ptrtoint ptr addrspace(2) %q to i64
+ %sub = sub i64 %p.int, %q.int
+ ret i64 %sub
+}
+
+; External state but stable representation: the address bits can be extracted
+; via ptrtoaddr, so the difference can be modeled.
+define i64 @sub_ptrtoint_external_state(ptr addrspace(3) %p, ptr addrspace(3) %q) {
+; CHECK-LABEL: 'sub_ptrtoint_external_state'
+; CHECK-NEXT: Classifying expressions for: @sub_ptrtoint_external_state
+; CHECK-NEXT: %p.int = ptrtoint ptr addrspace(3) %p to i64
+; CHECK-NEXT: --> %p.int U: full-set S: full-set
+; CHECK-NEXT: %q.int = ptrtoint ptr addrspace(3) %q to i64
+; CHECK-NEXT: --> %q.int U: full-set S: full-set
+; CHECK-NEXT: %sub = sub i64 %p.int, %q.int
+; CHECK-NEXT: --> ((-1 * (ptrtoaddr ptr addrspace(3) %q to i64)) + (ptrtoaddr ptr addrspace(3) %p to i64)) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @sub_ptrtoint_external_state
+;
+ %p.int = ptrtoint ptr addrspace(3) %p to i64
+ %q.int = ptrtoint ptr addrspace(3) %q to i64
+ %sub = sub i64 %p.int, %q.int
+ ret i64 %sub
+}
+
+; ptrtoint of a pointer add-rec to the address width is modeled via ptrtoaddr.
+define void @ptrtoint_addrec_fat_to_addrwidth(ptr addrspace(1) %in) {
+; CHECK-LABEL: 'ptrtoint_addrec_fat_to_addrwidth'
+; CHECK-NEXT: Classifying expressions for: @ptrtoint_addrec_fat_to_addrwidth
+; CHECK-NEXT: %iv = phi ptr addrspace(1) [ %in, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {%in,+,4}<nuw><%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.int = ptrtoint ptr addrspace(1) %iv to i64
+; CHECK-NEXT: --> {(ptrtoaddr ptr addrspace(1) %in to i64),+,4}<nuw><%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 1
+; CHECK-NEXT: --> {(4 + %in),+,4}<nw><%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %c = call i1 @cond()
+; CHECK-NEXT: --> %c U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @ptrtoint_addrec_fat_to_addrwidth
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi ptr addrspace(1) [ %in, %entry ], [ %iv.next, %loop ]
+ %iv.int = ptrtoint ptr addrspace(1) %iv to i64
+ %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 1
+ %c = call i1 @cond()
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; ptrtoint of a pointer add-rec on an unstable pointer must stay opaque.
+define void @ptrtoint_addrec_unstable(ptr addrspace(2) %in) {
+; CHECK-LABEL: 'ptrtoint_addrec_unstable'
+; CHECK-NEXT: Classifying expressions for: @ptrtoint_addrec_unstable
+; CHECK-NEXT: %iv = phi ptr addrspace(2) [ %in, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {%in,+,4}<nuw><%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.int = ptrtoint ptr addrspace(2) %iv to i64
+; CHECK-NEXT: --> %iv.int U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: %iv.next = getelementptr inbounds i32, ptr addrspace(2) %iv, i64 1
+; CHECK-NEXT: --> {(4 + %in),+,4}<nw><%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %c = call i1 @cond()
+; CHECK-NEXT: --> %c U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @ptrtoint_addrec_unstable
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable symbolic max backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi ptr addrspace(2) [ %in, %entry ], [ %iv.next, %loop ]
+ %iv.int = ptrtoint ptr addrspace(2) %iv to i64
+ %iv.next = getelementptr inbounds i32, ptr addrspace(2) %iv, i64 1
+ %c = call i1 @cond()
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; ptrtoint to i32 (narrower than the 64-bit address).
+define i32 @sub_ptrtoint_narrower_than_addr(ptr addrspace(1) %p, ptr addrspace(1) %q) {
+; CHECK-LABEL: 'sub_ptrtoint_narrower_than_addr'
+; CHECK-NEXT: Classifying expressions for: @sub_ptrtoint_narrower_than_addr
+; CHECK-NEXT: %p.int = ptrtoint ptr addrspace(1) %p to i32
+; CHECK-NEXT: --> %p.int U: full-set S: full-set
+; CHECK-NEXT: %q.int = ptrtoint ptr addrspace(1) %q to i32
+; CHECK-NEXT: --> %q.int U: full-set S: full-set
+; CHECK-NEXT: %sub = sub i32 %p.int, %q.int
+; CHECK-NEXT: --> ((trunc i64 (ptrtoaddr ptr addrspace(1) %p to i64) to i32) + (-1 * (trunc i64 (ptrtoaddr ptr addrspace(1) %q to i64) to i32))) U: full-set S: full-set
+; CHECK-NEXT: Determining loop execution counts for: @sub_ptrtoint_narrower_than_addr
+;
+ %p.int = ptrtoint ptr addrspace(1) %p to i32
+ %q.int = ptrtoint ptr addrspace(1) %q to i32
+ %sub = sub i32 %p.int, %q.int
+ ret i32 %sub
+}
+
+; The backedge-taken count of a loop iterating over a 128 bit fat pointer.
+define void @fat_ptr_ult_backedge_count(ptr addrspace(1) %start, ptr addrspace(1) %end) {
+; CHECK-LABEL: 'fat_ptr_ult_backedge_count'
+; CHECK-NEXT: Classifying expressions for: @fat_ptr_ult_backedge_count
+; CHECK-NEXT: %iv = phi ptr addrspace(1) [ %start, %entry ], [ %iv.next, %loop ]
+; CHECK-NEXT: --> {%start,+,4}<nuw><%loop> U: full-set S: full-set Exits: ((4 * ((-1 + (-1 * (ptrtoaddr ptr addrspace(1) %start to i64)) + ((4 + (ptrtoaddr ptr addrspace(1) %start to i64))<nuw> umax (ptrtoaddr ptr addrspace(1) %end to i64))) /u 4))<nuw> + %start) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 1
+; CHECK-NEXT: --> {(4 + %start)<nuw>,+,4}<nuw><%loop> U: [4,0) S: [4,0) Exits: (4 + (4 * ((-1 + (-1 * (ptrtoaddr ptr addrspace(1) %start to i64)) + ((4 + (ptrtoaddr ptr addrspace(1) %start to i64))<nuw> umax (ptrtoaddr ptr addrspace(1) %end to i64))) /u 4))<nuw> + %start) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @fat_ptr_ult_backedge_count
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 + (-1 * (ptrtoaddr ptr addrspace(1) %start to i64)) + ((4 + (ptrtoaddr ptr addrspace(1) %start to i64))<nuw> umax (ptrtoaddr ptr addrspace(1) %end to i64))) /u 4)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i64 4611686018427387902
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 + (-1 * (ptrtoaddr ptr addrspace(1) %start to i64)) + ((4 + (ptrtoaddr ptr addrspace(1) %start to i64))<nuw> umax (ptrtoaddr ptr addrspace(1) %end to i64))) /u 4)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi ptr addrspace(1) [ %start, %entry ], [ %iv.next, %loop ]
+ %iv.next = getelementptr inbounds i32, ptr addrspace(1) %iv, i64 1
+ %c = icmp ult ptr addrspace(1) %iv.next, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+declare i1 @cond()
diff --git a/llvm/test/Transforms/GVN/ptrtoaddr.ll b/llvm/test/Transforms/GVN/ptrtoaddr.ll
index 6d02bc67a820b..a731ef433741a 100644
--- a/llvm/test/Transforms/GVN/ptrtoaddr.ll
+++ b/llvm/test/Transforms/GVN/ptrtoaddr.ll
@@ -28,3 +28,49 @@ define i64 @ptrtoaddr_different(ptr %p, ptr %p2) {
%sub = sub i64 %i, %j
ret i64 %sub
}
+
+define i64 @ptrtoaddr_reuses_dominating_ptrtoint(ptr %p) {
+; CHECK-LABEL: define i64 @ptrtoaddr_reuses_dominating_ptrtoint(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[A:%.*]] = ptrtoaddr ptr [[P]] to i64
+; CHECK-NEXT: [[R:%.*]] = add i64 [[I]], [[A]]
+; CHECK-NEXT: ret i64 [[R]]
+;
+ %i = ptrtoint ptr %p to i64
+ %a = ptrtoaddr ptr %p to i64
+ %r = add i64 %i, %a
+ ret i64 %r
+}
+
+define i64 @ptrtoint_not_replaced_by_dominating_ptrtoaddr(ptr %p) {
+; CHECK-LABEL: define i64 @ptrtoint_not_replaced_by_dominating_ptrtoaddr(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = ptrtoaddr ptr [[P]] to i64
+; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[R:%.*]] = add i64 [[A]], [[I]]
+; CHECK-NEXT: ret i64 [[R]]
+;
+ %a = ptrtoaddr ptr %p to i64
+ %i = ptrtoint ptr %p to i64
+ %r = add i64 %a, %i
+ ret i64 %r
+}
+
+; A ptrtoaddr to a narrower type than the dominating ptrtoint is not a
+; replacement candidate (different values).
+define i64 @ptrtoaddr_wrong_type(ptr %p) {
+; CHECK-LABEL: define i64 @ptrtoaddr_wrong_type(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[P]] to i32
+; CHECK-NEXT: [[A:%.*]] = ptrtoaddr ptr [[P]] to i64
+; CHECK-NEXT: [[Z:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT: [[R:%.*]] = add i64 [[Z]], [[A]]
+; CHECK-NEXT: ret i64 [[R]]
+;
+ %i = ptrtoint ptr %p to i32
+ %a = ptrtoaddr ptr %p to i64
+ %z = zext i32 %i to i64
+ %r = add i64 %z, %a
+ ret i64 %r
+}
diff --git a/llvm/test/Transforms/IndVarSimplify/AArch64/expand-ptrtoaddr-reuse-ptrtoint.ll b/llvm/test/Transforms/IndVarSimplify/AArch64/expand-ptrtoaddr-reuse-ptrtoint.ll
new file mode 100644
index 0000000000000..30afcf6237067
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/AArch64/expand-ptrtoaddr-reuse-ptrtoint.ll
@@ -0,0 +1,46 @@
+; RUN: opt -passes='indvars' -S %s | FileCheck %s
+
+target triple = "arm64-apple-macosx"
+
+define i64 @count_then_convert(ptr %end) {
+; CHECK-LABEL: @count_then_convert(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[END1:%.*]] = ptrtoint ptr [[END:%.*]] to i64
+; CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq ptr [[END]], null
+; CHECK-NEXT: br i1 [[ISEMPTY]], label [[EXIT:%.*]], label [[BODY_PH:%.*]]
+; CHECK: body.ph:
+; CHECK-NEXT: br label [[BODY:%.*]]
+; CHECK: body:
+; CHECK-NEXT: br i1 true, label [[EXIT_LOOPEXIT:%.*]], label [[BODY]]
+; CHECK: exit.loopexit:
+; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[END1]], -8
+; CHECK-NEXT: [[TMP1:%.*]] = lshr i64 [[TMP0]], 3
+; CHECK-NEXT: [[TMP2:%.*]] = add nuw nsw i64 [[TMP1]], 1
+; CHECK-NEXT: br label [[EXIT]]
+; CHECK: exit:
+; CHECK-NEXT: [[CNT:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TMP2]], [[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT: [[ENDI:%.*]] = ptrtoint ptr [[END]] to i64
+; CHECK-NEXT: [[R:%.*]] = or i64 [[CNT]], [[ENDI]]
+; CHECK-NEXT: ret i64 [[R]]
+;
+entry:
+ %isempty = icmp eq ptr %end, null
+ br i1 %isempty, label %exit, label %body.ph
+
+body.ph:
+ br label %body
+
+body:
+ %p = phi ptr [ null, %body.ph ], [ %pn, %body ]
+ %n = phi i64 [ 0, %body.ph ], [ %nn, %body ]
+ %nn = add i64 %n, 1
+ %pn = getelementptr inbounds nuw i8, ptr %p, i64 8
+ %done = icmp eq ptr %pn, %end
+ br i1 %done, label %exit, label %body
+
+exit:
+ %cnt = phi i64 [ 0, %entry ], [ %nn, %body ]
+ %endi = ptrtoint ptr %end to i64
+ %r = or i64 %cnt, %endi
+ ret i64 %r
+}
More information about the llvm-commits
mailing list