[llvm] [IRBuilder] Handle byte types in CreateBitPreservingCastChain (PR #209557)

Anshil Gandhi via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 11:55:29 PDT 2026


https://github.com/gandhi56 updated https://github.com/llvm/llvm-project/pull/209557

>From 97dd9a7414f2d3405905a67fb73235e0707dcd1b Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <Anshil.Gandhi at amd.com>
Date: Tue, 14 Jul 2026 13:52:29 -0500
Subject: [PATCH 1/2] [AMDGPU] Precommit test for byte-vector <-> pointer
 promote-alloca casts

Add a regression test that promotes allocas to a byte vector and
converts between it and a same-sized pointer. This currently produces
an invalid direct `bitcast <2 x b32> to ptr`/`bitcast ptr to <2 x b32>`
via CreateBitPreservingCastChain, which trips the MachineVerifier
("bitcast cannot convert between pointers and other types"). A
follow-up change fixes CreateBitPreservingCastChain and updates this
test's checks.

Co-authored-by: Cursor <cursoragent at cursor.com>
---
 .../AMDGPU/promote-alloca-byte-ptr-cast.ll    | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll

diff --git a/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll b/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
new file mode 100644
index 0000000000000..04c1f020e8a68
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -passes=amdgpu-promote-alloca < %s | FileCheck %s
+
+; Promoting these allocas to a byte vector forces a conversion between a
+; byte-vector and a pointer of the same size. Byte types are not integers, so
+; CreateBitPreservingCastChain currently produces a direct (and invalid)
+; `bitcast <2 x b32> to ptr`, which later trips the MachineVerifier ("bitcast
+; cannot convert between pointers and other types"). A follow-up change
+; teaches CreateBitPreservingCastChain to route the conversion through a
+; pointer-sized integer via inttoptr/ptrtoint instead.
+
+target datalayout = "e-p:64:64-p5:32:32-A5"
+
+define amdgpu_kernel void @ptr_from_byte_vec(ptr %out, ptr %inptr) {
+; CHECK-LABEL: @ptr_from_byte_vec(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = freeze <2 x b32> poison
+; CHECK-NEXT:    [[PBITS:%.*]] = load b64, ptr [[INPTR:%.*]], align 8
+; CHECK-NEXT:    [[TMP0:%.*]] = bitcast b64 [[PBITS]] to <2 x b32>
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <2 x b32> [[TMP0]] to ptr
+; CHECK-NEXT:    store ptr [[TMP1]], ptr [[OUT:%.*]], align 8
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = alloca [2 x b32], align 8, addrspace(5)
+  %pbits = load b64, ptr %inptr, align 8
+  store b64 %pbits, ptr addrspace(5) %a, align 8
+  %p = load ptr, ptr addrspace(5) %a, align 8
+  store ptr %p, ptr %out, align 8
+  ret void
+}
+
+define amdgpu_kernel void @byte_vec_from_ptr(ptr %out, ptr %argp) {
+; CHECK-LABEL: @byte_vec_from_ptr(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[A:%.*]] = freeze <2 x b32> poison
+; CHECK-NEXT:    [[TMP0:%.*]] = bitcast ptr [[ARGP:%.*]] to <2 x b32>
+; CHECK-NEXT:    store <2 x b32> [[TMP0]], ptr [[OUT:%.*]], align 8
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = alloca [2 x b32], align 8, addrspace(5)
+  store ptr %argp, ptr addrspace(5) %a, align 8
+  %v = load <2 x b32>, ptr addrspace(5) %a, align 8
+  store <2 x b32> %v, ptr %out, align 8
+  ret void
+}

>From 8739022ab32784781fd0e2bbe1cd78fceed4067b Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <Anshil.Gandhi at amd.com>
Date: Tue, 14 Jul 2026 13:54:40 -0500
Subject: [PATCH 2/2] [IRBuilder] Handle byte types in
 CreateBitPreservingCastChain

CreateBitPreservingCastChain only routed conversions through an
inttoptr/ptrtoint step when the non-pointer side was an integer or
integer vector. Byte and byte-vector types are not integer types, so a
byte/byte-vector to pointer conversion fell through to a plain bitcast,
producing an invalid bitcast between a non-pointer aggregate and a
pointer (e.g. `bitcast <2 x b32> to ptr`). This manifested as a
MachineVerifier "bitcast cannot convert between pointers and other
types" failure once a pass fed byte element types into the cast chain
(see the preceding precommit test).

Treat byte and byte-vector types the same as integers here so the
conversion goes through a pointer-sized integer. Update
promote-alloca-byte-ptr-cast.ll's checks to reflect the fixed codegen
and add an IRBuilder unit test covering the same byte-vector <->
pointer conversions directly.

Co-authored-by: Cursor <cursoragent at cursor.com>
---
 llvm/lib/IR/IRBuilder.cpp                     | 14 +++++++++++--
 .../AMDGPU/promote-alloca-byte-ptr-cast.ll    | 20 ++++++++++---------
 llvm/unittests/IR/IRBuilderTest.cpp           | 19 ++++++++++++++++++
 3 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index df738faad7842..b8fe6233f924f 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -139,8 +139,16 @@ Value *IRBuilderBase::CreateBitPreservingCastChain(const DataLayout &DL,
     return CreateBitCast(In, Ty);
   };
 
+  // Byte (and byte-vector) types are not integers, but like integers they can
+  // be reinterpreted as a pointer-sized integer before/after an inttoptr or
+  // ptrtoint step. Treat them the same as integers here so we never attempt a
+  // direct (and invalid) bitcast between a byte/integer aggregate and a
+  // pointer.
+  bool OldIsIntLike =
+      OldTy->isIntOrIntVectorTy() || OldTy->isByteOrByteVectorTy();
+
   // See if we need inttoptr for this type pair. May require additional bitcast.
-  if (OldTy->isIntOrIntVectorTy() && NewTy->isPtrOrPtrVectorTy()) {
+  if (OldIsIntLike && NewTy->isPtrOrPtrVectorTy()) {
     // Expand <2 x i32> to i8* --> <2 x i32> to i64 to i8*
     // Expand i128 to <2 x i8*> --> i128 to <2 x i64> to <2 x i8*>
     // Expand <4 x i32> to <2 x i8*> --> <4 x i32> to <2 x i64> to <2 x i8*>
@@ -149,7 +157,9 @@ Value *IRBuilderBase::CreateBitPreservingCastChain(const DataLayout &DL,
   }
 
   // See if we need ptrtoint for this type pair. May require additional bitcast.
-  if (OldTy->isPtrOrPtrVectorTy() && NewTy->isIntOrIntVectorTy()) {
+  bool NewIsIntLike =
+      NewTy->isIntOrIntVectorTy() || NewTy->isByteOrByteVectorTy();
+  if (OldTy->isPtrOrPtrVectorTy() && NewIsIntLike) {
     // Expand <2 x i8*> to i128 --> <2 x i8*> to <2 x i64> to i128
     // Expand i8* to <2 x i32> --> i8* to i64 to <2 x i32>
     // Expand <2 x i8*> to <4 x i32> --> <2 x i8*> to <2 x i64> to <4 x i32>
diff --git a/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll b/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
index 04c1f020e8a68..5a4992d19407e 100644
--- a/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
+++ b/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
@@ -3,11 +3,11 @@
 
 ; Promoting these allocas to a byte vector forces a conversion between a
 ; byte-vector and a pointer of the same size. Byte types are not integers, so
-; CreateBitPreservingCastChain currently produces a direct (and invalid)
-; `bitcast <2 x b32> to ptr`, which later trips the MachineVerifier ("bitcast
-; cannot convert between pointers and other types"). A follow-up change
-; teaches CreateBitPreservingCastChain to route the conversion through a
-; pointer-sized integer via inttoptr/ptrtoint instead.
+; before CreateBitPreservingCastChain learned to treat them like integers this
+; produced a direct (and invalid) `bitcast <2 x b32> to ptr`, which later
+; tripped the MachineVerifier ("bitcast cannot convert between pointers and
+; other types"). The conversion must instead route through a pointer-sized
+; integer via inttoptr/ptrtoint.
 
 target datalayout = "e-p:64:64-p5:32:32-A5"
 
@@ -17,8 +17,9 @@ define amdgpu_kernel void @ptr_from_byte_vec(ptr %out, ptr %inptr) {
 ; CHECK-NEXT:    [[A:%.*]] = freeze <2 x b32> poison
 ; CHECK-NEXT:    [[PBITS:%.*]] = load b64, ptr [[INPTR:%.*]], align 8
 ; CHECK-NEXT:    [[TMP0:%.*]] = bitcast b64 [[PBITS]] to <2 x b32>
-; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <2 x b32> [[TMP0]] to ptr
-; CHECK-NEXT:    store ptr [[TMP1]], ptr [[OUT:%.*]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <2 x b32> [[TMP0]] to i64
+; CHECK-NEXT:    [[TMP2:%.*]] = inttoptr i64 [[TMP1]] to ptr
+; CHECK-NEXT:    store ptr [[TMP2]], ptr [[OUT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -34,8 +35,9 @@ define amdgpu_kernel void @byte_vec_from_ptr(ptr %out, ptr %argp) {
 ; CHECK-LABEL: @byte_vec_from_ptr(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[A:%.*]] = freeze <2 x b32> poison
-; CHECK-NEXT:    [[TMP0:%.*]] = bitcast ptr [[ARGP:%.*]] to <2 x b32>
-; CHECK-NEXT:    store <2 x b32> [[TMP0]], ptr [[OUT:%.*]], align 8
+; CHECK-NEXT:    [[TMP0:%.*]] = ptrtoint ptr [[ARGP:%.*]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast i64 [[TMP0]] to <2 x b32>
+; CHECK-NEXT:    store <2 x b32> [[TMP1]], ptr [[OUT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
 entry:
diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp
index 6befc403c6f30..41a56525de992 100644
--- a/llvm/unittests/IR/IRBuilderTest.cpp
+++ b/llvm/unittests/IR/IRBuilderTest.cpp
@@ -606,6 +606,25 @@ TEST_F(IRBuilderTest, GetIntTy) {
   EXPECT_EQ(IntPtrTy, IntegerType::get(Ctx, IntPtrBitSize));
 }
 
+TEST_F(IRBuilderTest, CreateBitPreservingCastChainByteTypes) {
+  M->setDataLayout("e-p:64:64");
+  IRBuilder<> Builder(BB);
+  const DataLayout &DL = M->getDataLayout();
+  Type *PtrTy = PointerType::getUnqual(Ctx);
+  Type *ByteVecTy = VectorType::get(Type::getByteNTy(Ctx, 32), 2,
+                                    /*Scalable=*/false);
+  Value *ByteVec =
+      Builder.CreateLoad(ByteVecTy, Constant::getNullValue(PtrTy));
+  Value *Ptr = Builder.CreateLoad(PtrTy, Constant::getNullValue(PtrTy));
+  Value *ToPtr = Builder.CreateBitPreservingCastChain(DL, ByteVec, PtrTy);
+  ASSERT_TRUE(isa<IntToPtrInst>(ToPtr));
+  EXPECT_TRUE(isa<BitCastInst>(cast<IntToPtrInst>(ToPtr)->getOperand(0)));
+  Value *ToByteVec = Builder.CreateBitPreservingCastChain(DL, Ptr, ByteVecTy);
+  ASSERT_EQ(ToByteVec->getType(), ByteVecTy);
+  ASSERT_TRUE(isa<BitCastInst>(ToByteVec));
+  EXPECT_TRUE(isa<PtrToIntInst>(cast<BitCastInst>(ToByteVec)->getOperand(0)));
+}
+
 TEST_F(IRBuilderTest, UnaryOperators) {
   IRBuilder<NoFolder> Builder(BB);
   Value *V = Builder.CreateLoad(GV->getValueType(), GV);



More information about the llvm-commits mailing list