[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