[llvm] [IRBuilder] Handle byte types in CreateBitPreservingCastChain (PR #209557)
Anshil Gandhi via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 20:18:58 PDT 2026
https://github.com/gandhi56 updated https://github.com/llvm/llvm-project/pull/209557
>From f4713044b8c87767a296fb7d90bdb48fd2411574 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] [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.
Co-authored-by: Cursor <cursoragent at cursor.com>
---
llvm/include/llvm/IR/Type.h | 4 ++
llvm/lib/IR/IRBuilder.cpp | 6 ++-
llvm/lib/IR/Type.cpp | 4 ++
.../AMDGPU/promote-alloca-byte-ptr-cast.ll | 49 +++++++++++++++++++
llvm/unittests/IR/IRBuilderTest.cpp | 18 +++++++
5 files changed, 79 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h
index b0e949c118ce4..7a9816b74f987 100644
--- a/llvm/include/llvm/IR/Type.h
+++ b/llvm/include/llvm/IR/Type.h
@@ -298,6 +298,10 @@ class Type {
/// Determine if this type could be losslessly bitcast to Ty
LLVM_ABI bool canLosslesslyBitCastTo(Type *Ty) const;
+ /// Return true if this type could be converted through an int/int-vector
+ /// or a ptr/ptr-vector.
+ LLVM_ABI bool isCastableThroughIntOrPtr() const;
+
/// Return true if this type is empty, that is, it has no elements or all of
/// its elements are empty.
LLVM_ABI bool isEmptyTy() const;
diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp
index df738faad7842..8656450c8d5b5 100644
--- a/llvm/lib/IR/IRBuilder.cpp
+++ b/llvm/lib/IR/IRBuilder.cpp
@@ -140,7 +140,7 @@ Value *IRBuilderBase::CreateBitPreservingCastChain(const DataLayout &DL,
};
// See if we need inttoptr for this type pair. May require additional bitcast.
- if (OldTy->isIntOrIntVectorTy() && NewTy->isPtrOrPtrVectorTy()) {
+ if (OldTy->isCastableThroughIntOrPtr() && 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 +149,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/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp
index 890f523b69f55..597cecf830724 100644
--- a/llvm/lib/IR/Type.cpp
+++ b/llvm/lib/IR/Type.cpp
@@ -177,6 +177,10 @@ bool Type::canLosslesslyBitCastTo(Type *Ty) const {
return false;
}
+bool Type::isCastableThroughIntOrPtr() const {
+ return isIntOrIntVectorTy() || isByteOrByteVectorTy();
+}
+
bool Type::isEmptyTy() const {
if (auto *ATy = dyn_cast<ArrayType>(this)) {
unsigned NumElements = ATy->getNumElements();
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..76b70d0c7dafb
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/promote-alloca-byte-ptr-cast.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -passes=amdgpu-promote-alloca,verify < %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
+; 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"
+
+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 i64
+; CHECK-NEXT: [[TMP2:%.*]] = inttoptr i64 [[TMP1]] to ptr
+; CHECK-NEXT: store ptr [[TMP2]], 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:%.*]] = 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:
+ %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
+}
diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp
index 055687f9f0407..bea757cb0b1a2 100644
--- a/llvm/unittests/IR/IRBuilderTest.cpp
+++ b/llvm/unittests/IR/IRBuilderTest.cpp
@@ -606,6 +606,24 @@ 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