[llvm-branch-commits] [llvm] [IR] Fold `bitcast` pairs through bytes with `ptrtoaddr` (PR #229107)

Pedro Lobo via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 5 07:55:59 PDT 2026


https://github.com/pedroclobo created https://github.com/llvm/llvm-project/pull/229107

Fold `bitcast` pairs through bytes with `ptrtoaddr`.                                                                                                                       

Folds:
- A pointer-to-byte `bitcast`, followed by a `bitcast` to an integer becomes `ptrtoaddr`, as both return the address without exposing provenance.
- A byte-to-pointer `bitcast` and `ptrtoaddr` becomes a `bitcast`.

```diff
-%b = bitcast ptr %p to b64
-%x = bitcast b64 %b to i64
+%x = ptrtoaddr ptr %p to i64

-%q = bitcast b64 %b to ptr
-%y = ptrtoaddr ptr %q to i64
+%y = bitcast b64 %b to i64
```

>From 8e0a797b0a77481655ee052dc0754878954e506f Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 11:31:34 +0100
Subject: [PATCH 1/2] [IR] Pre-commit tests for folding `bitcast` pairs through
 bytes with `ptrtoaddr`

---
 .../Transforms/InstCombine/byte-cast-pairs.ll | 103 +++++++++++++++++-
 .../InstSimplify/byte-cast-pairs.ll           |   4 +-
 2 files changed, 99 insertions(+), 8 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll b/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
index 64b76b9270809..cbf86fa5db305 100644
--- a/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
+++ b/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
@@ -1,8 +1,10 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
-define i64 @neg_ptr_to_byte_to_int(ptr %p) {
-; CHECK-LABEL: define i64 @neg_ptr_to_byte_to_int(
+target datalayout = "p1:128:128:128:64"
+
+define i64 @ptr_to_byte_to_int(ptr %p) {
+; CHECK-LABEL: define i64 @ptr_to_byte_to_int(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to b64
 ; CHECK-NEXT:    [[X:%.*]] = bitcast b64 [[B]] to i64
@@ -25,8 +27,8 @@ define ptr @neg_int_to_byte_to_ptr(i64 %i) {
   ret ptr %x
 }
 
-define <2 x i64> @neg_ptr_to_byte_to_int_vec(<2 x ptr> %p) {
-; CHECK-LABEL: define <2 x i64> @neg_ptr_to_byte_to_int_vec(
+define <2 x i64> @ptr_to_byte_to_int_vec(<2 x ptr> %p) {
+; CHECK-LABEL: define <2 x i64> @ptr_to_byte_to_int_vec(
 ; CHECK-SAME: <2 x ptr> [[P:%.*]]) {
 ; CHECK-NEXT:    [[B:%.*]] = bitcast <2 x ptr> [[P]] to <2 x b64>
 ; CHECK-NEXT:    [[X:%.*]] = bitcast <2 x b64> [[B]] to <2 x i64>
@@ -37,6 +39,69 @@ define <2 x i64> @neg_ptr_to_byte_to_int_vec(<2 x ptr> %p) {
   ret <2 x i64> %x
 }
 
+define i64 @ptr_to_byte_vec_to_int(ptr %p) {
+; CHECK-LABEL: define i64 @ptr_to_byte_vec_to_int(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to <2 x b32>
+; CHECK-NEXT:    [[X:%.*]] = bitcast <2 x b32> [[B]] to i64
+; CHECK-NEXT:    ret i64 [[X]]
+;
+  %b = bitcast ptr %p to <2 x b32>
+  %x = bitcast <2 x b32> %b to i64
+  ret i64 %x
+}
+
+; ptrtoaddr can't cast a scalar pointer to a vector.
+define <1 x i64> @neg_ptr_to_byte_to_int_vec1(ptr %p) {
+; CHECK-LABEL: define <1 x i64> @neg_ptr_to_byte_to_int_vec1(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to b64
+; CHECK-NEXT:    [[X:%.*]] = bitcast b64 [[B]] to <1 x i64>
+; CHECK-NEXT:    ret <1 x i64> [[X]]
+;
+  %b = bitcast ptr %p to b64
+  %x = bitcast b64 %b to <1 x i64>
+  ret <1 x i64> %x
+}
+
+define double @neg_ptr_to_byte_to_fp(ptr %p) {
+; CHECK-LABEL: define double @neg_ptr_to_byte_to_fp(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to b64
+; CHECK-NEXT:    [[X:%.*]] = bitcast b64 [[B]] to double
+; CHECK-NEXT:    ret double [[X]]
+;
+  %b = bitcast ptr %p to b64
+  %x = bitcast b64 %b to double
+  ret double %x
+}
+
+; Address width (64) != pointer width (128).
+define i128 @neg_ptr_to_byte_to_int_narrow_addr(ptr addrspace(1) %p) {
+; CHECK-LABEL: define i128 @neg_ptr_to_byte_to_int_narrow_addr(
+; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr addrspace(1) [[P]] to b128
+; CHECK-NEXT:    [[X:%.*]] = bitcast b128 [[B]] to i128
+; CHECK-NEXT:    ret i128 [[X]]
+;
+  %b = bitcast ptr addrspace(1) %p to b128
+  %x = bitcast b128 %b to i128
+  ret i128 %x
+}
+
+; Lane width matches the address width (64), but not the pointer width (128).
+define <2 x i64> @neg_ptr_to_byte_to_int_narrow_addr_vec(ptr addrspace(1) %p) {
+; CHECK-LABEL: define <2 x i64> @neg_ptr_to_byte_to_int_narrow_addr_vec(
+; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr addrspace(1) [[P]] to b128
+; CHECK-NEXT:    [[X:%.*]] = bitcast b128 [[B]] to <2 x i64>
+; CHECK-NEXT:    ret <2 x i64> [[X]]
+;
+  %b = bitcast ptr addrspace(1) %p to b128
+  %x = bitcast b128 %b to <2 x i64>
+  ret <2 x i64> %x
+}
+
 define ptr @neg_ptr_to_byte_to_ptr_addrspace(ptr addrspace(2) %p) {
 ; CHECK-LABEL: define ptr @neg_ptr_to_byte_to_ptr_addrspace(
 ; CHECK-SAME: ptr addrspace(2) [[P:%.*]]) {
@@ -61,6 +126,7 @@ define b64 @neg_inttoptr_to_byte(i64 %i) {
   ret b64 %x
 }
 
+; ptrtoint exposes the provenance.
 define i64 @neg_byte_to_ptrtoint(b64 %b) {
 ; CHECK-LABEL: define i64 @neg_byte_to_ptrtoint(
 ; CHECK-SAME: b64 [[B:%.*]]) {
@@ -73,8 +139,8 @@ define i64 @neg_byte_to_ptrtoint(b64 %b) {
   ret i64 %x
 }
 
-define i64 @neg_byte_to_ptrtoaddr(b64 %b) {
-; CHECK-LABEL: define i64 @neg_byte_to_ptrtoaddr(
+define i64 @byte_to_ptrtoaddr(b64 %b) {
+; CHECK-LABEL: define i64 @byte_to_ptrtoaddr(
 ; CHECK-SAME: b64 [[B:%.*]]) {
 ; CHECK-NEXT:    [[P:%.*]] = bitcast b64 [[B]] to ptr
 ; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr ptr [[P]] to i64
@@ -85,6 +151,31 @@ define i64 @neg_byte_to_ptrtoaddr(b64 %b) {
   ret i64 %x
 }
 
+define <2 x i64> @byte_to_ptrtoaddr_vec(<2 x b64> %b) {
+; CHECK-LABEL: define <2 x i64> @byte_to_ptrtoaddr_vec(
+; CHECK-SAME: <2 x b64> [[B:%.*]]) {
+; CHECK-NEXT:    [[P:%.*]] = bitcast <2 x b64> [[B]] to <2 x ptr>
+; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr <2 x ptr> [[P]] to <2 x i64>
+; CHECK-NEXT:    ret <2 x i64> [[X]]
+;
+  %p = bitcast <2 x b64> %b to <2 x ptr>
+  %x = ptrtoaddr <2 x ptr> %p to <2 x i64>
+  ret <2 x i64> %x
+}
+
+; Address width (64) != pointer width (128).
+define i64 @neg_byte_to_ptrtoaddr_narrow_addr(b128 %b) {
+; CHECK-LABEL: define i64 @neg_byte_to_ptrtoaddr_narrow_addr(
+; CHECK-SAME: b128 [[B:%.*]]) {
+; CHECK-NEXT:    [[P:%.*]] = bitcast b128 [[B]] to ptr addrspace(1)
+; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr ptr addrspace(1) [[P]] to i64
+; CHECK-NEXT:    ret i64 [[X]]
+;
+  %p = bitcast b128 %b to ptr addrspace(1)
+  %x = ptrtoaddr ptr addrspace(1) %p to i64
+  ret i64 %x
+}
+
 define ptr @ptr_to_byte_to_ptr(ptr %p) {
 ; CHECK-LABEL: define ptr @ptr_to_byte_to_ptr(
 ; CHECK-SAME: ptr [[P:%.*]]) {
diff --git a/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll b/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
index 578a51297d88e..f4d27aa32db66 100644
--- a/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
+++ b/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
@@ -9,8 +9,8 @@
 ; CHECK: @g = external global i32
 ; CHECK: @c = global i64 bitcast (b64 bitcast (ptr @g to b64) to i64)
 ;.
-define i64 @neg_ptr_to_byte_to_int_const() {
-; CHECK-LABEL: define i64 @neg_ptr_to_byte_to_int_const() {
+define i64 @ptr_to_byte_to_int_const() {
+; CHECK-LABEL: define i64 @ptr_to_byte_to_int_const() {
 ; CHECK-NEXT:    ret i64 bitcast (b64 bitcast (ptr @g to b64) to i64)
 ;
   %x = bitcast b64 bitcast (ptr @g to b64) to i64

>From 2e20133af5d0156fe65d783973722902faf4c928 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Mon, 5 Oct 2026 11:36:50 +0100
Subject: [PATCH 2/2] [IR] Fold `bitcast` pairs through bytes with `ptrtoaddr`

---
 llvm/lib/IR/Instructions.cpp                  | 24 +++++++++++++++----
 llvm/test/Transforms/GVN/pr207532.ll          |  2 +-
 .../Transforms/InstCombine/byte-cast-pairs.ll | 15 ++++--------
 .../InstSimplify/byte-cast-pairs.ll           |  2 +-
 4 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 402540349af06..dd051d51b4db8 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -3143,11 +3143,18 @@ unsigned CastInst::isEliminableCastPair(Instruction::CastOps firstOp,
       // FIXME: this state can be merged with (2), but the following assert
       // is useful to check the correcteness of the sequence due to semantic
       // change of bitcast.
-      // ptrtoint/ptrtoaddr can only fold through a bitcast if the source was
-      // already a pointer. A byte-to-pointer bitcast must stay as a separate
-      // bitcast.
-      if (!SrcTy->isPtrOrPtrVectorTy())
+      // bitcast, ptrtoaddr -> bitcast, if SrcTy is a byte and the address is
+      //                                the whole pointer
+      // ptrtoint can only fold through a bitcast if the source was already a
+      // pointer, as ptrtoint exposes the provenance.
+      if (!SrcTy->isPtrOrPtrVectorTy()) {
+        // ptrtoaddr yields the address width, so the sizes only match if the
+        // address is the whole pointer.
+        if (secondOp == Instruction::PtrToAddr &&
+            SrcTy->getPrimitiveSizeInBits() == DstTy->getPrimitiveSizeInBits())
+          return Instruction::BitCast;
         return 0;
+      }
       assert(
         SrcTy->isPtrOrPtrVectorTy() &&
         MidTy->isPtrOrPtrVectorTy() &&
@@ -3163,6 +3170,8 @@ unsigned CastInst::isEliminableCastPair(Instruction::CastOps firstOp,
       // bitcast, bitcast -> bitcast, if neither SrcTy nor DstTy is a pointer,
       //                              both are pointers in the same addrspace,
       //                              or one is a pointer and the other a byte
+      // bitcast, bitcast -> ptrtoaddr, if a pointer goes through a byte to an
+      //                                integer of the address width
       // A pair of bitcasts through a byte must stay separate otherwise.
       if (!SrcTy->isPtrOrPtrVectorTy() && !DstTy->isPtrOrPtrVectorTy())
         return Instruction::BitCast;
@@ -3171,6 +3180,13 @@ unsigned CastInst::isEliminableCastPair(Instruction::CastOps firstOp,
         return Instruction::BitCast;
       if (SrcTy->isByteOrByteVectorTy() || DstTy->isByteOrByteVectorTy())
         return Instruction::BitCast;
+      if (SrcTy->isPtrOrPtrVectorTy() && DstTy->isIntOrIntVectorTy() &&
+          SrcTy->isVectorTy() == DstTy->isVectorTy() && DL) {
+        unsigned AddrSize = DL->getAddressSizeInBits(SrcTy);
+        if (AddrSize == DL->getPointerTypeSizeInBits(SrcTy) &&
+            AddrSize == DstTy->getScalarSizeInBits())
+          return Instruction::PtrToAddr;
+      }
       return 0;
     case 99:
       // Cast combination can't happen (error in input). This is for all cases
diff --git a/llvm/test/Transforms/GVN/pr207532.ll b/llvm/test/Transforms/GVN/pr207532.ll
index f1db0fdff9a2f..122a6cf47def6 100644
--- a/llvm/test/Transforms/GVN/pr207532.ll
+++ b/llvm/test/Transforms/GVN/pr207532.ll
@@ -9,7 +9,7 @@ define ptr @forward_byte_store_to_ptr_load() {
 ; CHECK-LABEL: define ptr @forward_byte_store_to_ptr_load() {
 ; CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
 ; CHECK-NEXT:    store b64 bitcast (ptr @g to b64), ptr [[A]], align 8
-; CHECK-NEXT:    ret ptr inttoptr (i64 bitcast (b64 bitcast (ptr @g to b64) to i64) to ptr)
+; CHECK-NEXT:    ret ptr inttoptr (i64 ptrtoaddr (ptr @g to i64) to ptr)
 ;
   %a = alloca ptr, align 8
   store b64 bitcast (ptr @g to b64), ptr %a, align 8
diff --git a/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll b/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
index cbf86fa5db305..3531b5b822877 100644
--- a/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
+++ b/llvm/test/Transforms/InstCombine/byte-cast-pairs.ll
@@ -6,8 +6,7 @@ target datalayout = "p1:128:128:128:64"
 define i64 @ptr_to_byte_to_int(ptr %p) {
 ; CHECK-LABEL: define i64 @ptr_to_byte_to_int(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to b64
-; CHECK-NEXT:    [[X:%.*]] = bitcast b64 [[B]] to i64
+; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr ptr [[P]] to i64
 ; CHECK-NEXT:    ret i64 [[X]]
 ;
   %b = bitcast ptr %p to b64
@@ -30,8 +29,7 @@ define ptr @neg_int_to_byte_to_ptr(i64 %i) {
 define <2 x i64> @ptr_to_byte_to_int_vec(<2 x ptr> %p) {
 ; CHECK-LABEL: define <2 x i64> @ptr_to_byte_to_int_vec(
 ; CHECK-SAME: <2 x ptr> [[P:%.*]]) {
-; CHECK-NEXT:    [[B:%.*]] = bitcast <2 x ptr> [[P]] to <2 x b64>
-; CHECK-NEXT:    [[X:%.*]] = bitcast <2 x b64> [[B]] to <2 x i64>
+; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr <2 x ptr> [[P]] to <2 x i64>
 ; CHECK-NEXT:    ret <2 x i64> [[X]]
 ;
   %b = bitcast <2 x ptr> %p to <2 x b64>
@@ -42,8 +40,7 @@ define <2 x i64> @ptr_to_byte_to_int_vec(<2 x ptr> %p) {
 define i64 @ptr_to_byte_vec_to_int(ptr %p) {
 ; CHECK-LABEL: define i64 @ptr_to_byte_vec_to_int(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    [[B:%.*]] = bitcast ptr [[P]] to <2 x b32>
-; CHECK-NEXT:    [[X:%.*]] = bitcast <2 x b32> [[B]] to i64
+; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr ptr [[P]] to i64
 ; CHECK-NEXT:    ret i64 [[X]]
 ;
   %b = bitcast ptr %p to <2 x b32>
@@ -142,8 +139,7 @@ define i64 @neg_byte_to_ptrtoint(b64 %b) {
 define i64 @byte_to_ptrtoaddr(b64 %b) {
 ; CHECK-LABEL: define i64 @byte_to_ptrtoaddr(
 ; CHECK-SAME: b64 [[B:%.*]]) {
-; CHECK-NEXT:    [[P:%.*]] = bitcast b64 [[B]] to ptr
-; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr ptr [[P]] to i64
+; CHECK-NEXT:    [[X:%.*]] = bitcast b64 [[B]] to i64
 ; CHECK-NEXT:    ret i64 [[X]]
 ;
   %p = bitcast b64 %b to ptr
@@ -154,8 +150,7 @@ define i64 @byte_to_ptrtoaddr(b64 %b) {
 define <2 x i64> @byte_to_ptrtoaddr_vec(<2 x b64> %b) {
 ; CHECK-LABEL: define <2 x i64> @byte_to_ptrtoaddr_vec(
 ; CHECK-SAME: <2 x b64> [[B:%.*]]) {
-; CHECK-NEXT:    [[P:%.*]] = bitcast <2 x b64> [[B]] to <2 x ptr>
-; CHECK-NEXT:    [[X:%.*]] = ptrtoaddr <2 x ptr> [[P]] to <2 x i64>
+; CHECK-NEXT:    [[X:%.*]] = bitcast <2 x b64> [[B]] to <2 x i64>
 ; CHECK-NEXT:    ret <2 x i64> [[X]]
 ;
   %p = bitcast <2 x b64> %b to <2 x ptr>
diff --git a/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll b/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
index f4d27aa32db66..99831384cbe55 100644
--- a/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
+++ b/llvm/test/Transforms/InstSimplify/byte-cast-pairs.ll
@@ -11,7 +11,7 @@
 ;.
 define i64 @ptr_to_byte_to_int_const() {
 ; CHECK-LABEL: define i64 @ptr_to_byte_to_int_const() {
-; CHECK-NEXT:    ret i64 bitcast (b64 bitcast (ptr @g to b64) to i64)
+; CHECK-NEXT:    ret i64 ptrtoaddr (ptr @g to i64)
 ;
   %x = bitcast b64 bitcast (ptr @g to b64) to i64
   ret i64 %x



More information about the llvm-branch-commits mailing list