[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