[llvm] [GlobalISel][IRTranslator] Lower ptr/non-ptr bitcasts via G_INTTOPTR/G_PTRTOINT (PR #203335)
Anshil Gandhi via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 11:00:20 PDT 2026
https://github.com/gandhi56 updated https://github.com/llvm/llvm-project/pull/203335
>From dc5ccd1599a4a6782124761b84c7565c404a8896 Mon Sep 17 00:00:00 2001
From: Anshil Gandhi <Anshil.Gandhi at amd.com>
Date: Thu, 11 Jun 2026 11:04:58 -0500
Subject: [PATCH] [GlobalISel][IRTranslator] Lower ptr/non-ptr bitcasts via
G_INTTOPTR/G_PTRTOINT
IRTranslator::translateBitCast was emitting G_BITCAST across the pointer
boundary (e.g. <2 x s32> -> ptr on AMDGPU kernarg unpacking). MachineVerifier
requires G_BITCAST operands to be both pointers or both non-pointers, so
GlobalISel compilation aborted after IR translation on many AMDGPU tests.
For equal-sized ptr <-> non-ptr reinterprets, lower through G_INTTOPTR or
G_PTRTOINT instead. When the IR type is not already a pointer-sized integer
(or the vreg LLT already matches that integer), insert a G_BITCAST to/from
a pointer-sized scalar int first. Skip the intermediate G_BITCAST when the
source or destination is already that integer LLT to avoid invalid sN -> sN
bitcasts.
Update AMDGPU call-return-value checks and add Generic GlobalISel tests for
the two-step vector-byte <-> ptr lowering paths.
---
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 77 ++++++++++++++---
.../GlobalISel/irtranslator-byte-type.ll | 84 +++++++++++++++++++
2 files changed, 151 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 28755da8327aa..221a5b58cc4f4 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -1585,6 +1585,8 @@ bool IRTranslator::translateBitCast(const User &U,
MachineIRBuilder &MIRBuilder) {
Type *SrcTy = U.getOperand(0)->getType();
Type *DstTy = U.getType();
+ unsigned SrcTyBits = DL->getTypeSizeInBits(SrcTy);
+ unsigned DstTyBits = DL->getTypeSizeInBits(DstTy);
// If we're bitcasting to the source type, we can reuse the source vreg.
if (getLLTForType(*SrcTy, *DL) == getLLTForType(*DstTy, *DL)) {
@@ -1596,16 +1598,71 @@ bool IRTranslator::translateBitCast(const User &U,
return translateCopy(U, *U.getOperand(0), MIRBuilder);
}
- // Only the scalar byte<->ptr crossing is redirected to G_INTTOPTR/G_PTRTOINT,
- // which is the well-typed MIR shape for that boundary. Vector byte<->ptr
- // (e.g. <N x b32> -> ptr produced by mixed-type load coalescing) and other
- // legacy ptr/non-ptr IR bitcasts (AMDGPU iN<->p3 kernarg packing, etc.)
- // keep their historical G_BITCAST lowering — G_INTTOPTR has no vector-src
- // -> scalar-ptr form, and downstream passes already handle G_BITCAST.
- if (DstTy->isPointerTy() && SrcTy->isByteTy())
- return translateCast(TargetOpcode::G_INTTOPTR, U, MIRBuilder);
- if (SrcTy->isPointerTy() && DstTy->isByteTy())
- return translateCast(TargetOpcode::G_PTRTOINT, U, MIRBuilder);
+ // Emit G_INTTOPTR
+ if (!SrcTy->isPointerTy() && DstTy->isPointerTy()) {
+ if (DstTyBits == SrcTyBits) {
+ if (!mayTranslateUserTypes(U))
+ return false;
+
+ uint32_t Flags = 0;
+ if (const Instruction *I = dyn_cast<Instruction>(&U))
+ Flags = MachineInstr::copyFlagsFromInstruction(*I);
+
+ Register SrcReg = getOrCreateVReg(*U.getOperand(0));
+ Register DstReg = getOrCreateVReg(U);
+ MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+ LLT IntLLT = LLT::scalar(DstTyBits);
+ LLT SrcLLT = MRI.getType(SrcReg);
+
+ // Prefer IR integer check; also accept when the operand is already the
+ // pointer-sized scalar LLT (e.g. i64 value lowered to s64) so we never
+ // emit G_BITCAST sN -> sN, which MachineVerifier rejects.
+ if ((SrcTy->isIntegerTy() &&
+ cast<IntegerType>(SrcTy)->getBitWidth() == DstTyBits) ||
+ SrcLLT == IntLLT) {
+ MIRBuilder.buildInstr(TargetOpcode::G_INTTOPTR, {DstReg}, {SrcReg},
+ Flags);
+ return true;
+ }
+
+ auto IntVal = MIRBuilder.buildBitcast(IntLLT, SrcReg);
+ MIRBuilder.buildInstr(TargetOpcode::G_INTTOPTR, {DstReg},
+ {IntVal.getReg(0)}, Flags);
+ return true;
+ }
+ }
+
+ // Emit G_PTRTOINT for the Ptr -> Int case where the source
+ // and destination are equally sized. Otherwise, emit a G_BITCAST.
+ if (SrcTy->isPointerTy() && !DstTy->isPointerTy()) {
+ if (SrcTyBits == DstTyBits) {
+ if (!mayTranslateUserTypes(U))
+ return false;
+
+ uint32_t Flags = 0;
+ if (const Instruction *I = dyn_cast<Instruction>(&U))
+ Flags = MachineInstr::copyFlagsFromInstruction(*I);
+
+ Register SrcReg = getOrCreateVReg(*U.getOperand(0));
+ Register DstReg = getOrCreateVReg(U);
+ MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+ LLT IntLLT = LLT::scalar(SrcTyBits);
+ LLT DstLLT = MRI.getType(DstReg);
+
+ if ((DstTy->isIntegerTy() &&
+ cast<IntegerType>(DstTy)->getBitWidth() == SrcTyBits) ||
+ DstLLT == IntLLT) {
+ MIRBuilder.buildInstr(TargetOpcode::G_PTRTOINT, {DstReg}, {SrcReg},
+ Flags);
+ return true;
+ }
+
+ auto IntVal = MIRBuilder.buildPtrToInt(IntLLT, SrcReg);
+ MIRBuilder.buildInstr(TargetOpcode::G_BITCAST, {DstReg},
+ {IntVal.getReg(0)}, Flags);
+ return true;
+ }
+ }
return translateCast(TargetOpcode::G_BITCAST, U, MIRBuilder);
}
diff --git a/llvm/test/CodeGen/Generic/GlobalISel/irtranslator-byte-type.ll b/llvm/test/CodeGen/Generic/GlobalISel/irtranslator-byte-type.ll
index af6885a285240..01f8412cb7529 100644
--- a/llvm/test/CodeGen/Generic/GlobalISel/irtranslator-byte-type.ll
+++ b/llvm/test/CodeGen/Generic/GlobalISel/irtranslator-byte-type.ll
@@ -646,6 +646,90 @@ define ptr @bitcast_b64_to_p0(b64 %b) {
ret ptr %r
}
+; <2 x b32> is not an LLVM integer type, so lowering uses G_BITCAST to s64 then
+; G_INTTOPTR (case 2).
+define ptr @irtranslator_bitcast_v2b32_to_ptr(<2 x b32> %v) {
+ ; AARCH64-LABEL: name: irtranslator_bitcast_v2b32_to_ptr
+ ; AARCH64: bb.1 (%ir-block.0):
+ ; AARCH64-NEXT: liveins: $d0
+ ; AARCH64-NEXT: {{ $}}
+ ; AARCH64-NEXT: [[V:%[0-9]+]]:_(<2 x i32>) = COPY $d0
+ ; AARCH64-NEXT: [[I:%[0-9]+]]:_(s64) = G_BITCAST [[V]](<2 x i32>)
+ ; AARCH64-NEXT: [[P:%[0-9]+]]:_(p0) = G_INTTOPTR [[I]](s64)
+ ; AARCH64-NEXT: $x0 = COPY [[P]](p0)
+ ; AARCH64-NEXT: RET_ReallyLR implicit $x0
+ ;
+ ; AMDGPU-LABEL: name: irtranslator_bitcast_v2b32_to_ptr
+ ; AMDGPU: bb.1 (%ir-block.0):
+ ; AMDGPU-NEXT: liveins: $vgpr0, $vgpr1
+ ; AMDGPU-NEXT: {{ $}}
+ ; AMDGPU-NEXT: [[E0:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; AMDGPU-NEXT: [[E1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; AMDGPU-NEXT: [[V:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[E0]](s32), [[E1]](s32)
+ ; AMDGPU-NEXT: [[I:%[0-9]+]]:_(s64) = G_BITCAST [[V]](<2 x s32>)
+ ; AMDGPU-NEXT: [[P:%[0-9]+]]:_(p0) = G_INTTOPTR [[I]](s64)
+ ; AMDGPU-NEXT: [[U0:%[0-9]+]]:_(s32), [[U1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[P]](p0)
+ ; AMDGPU-NEXT: $vgpr0 = COPY [[U0]](s32)
+ ; AMDGPU-NEXT: $vgpr1 = COPY [[U1]](s32)
+ ; AMDGPU-NEXT: SI_RETURN implicit $vgpr0, implicit $vgpr1
+ ;
+ ; X86-LABEL: name: irtranslator_bitcast_v2b32_to_ptr
+ ; X86: bb.1 (%ir-block.0):
+ ; X86-NEXT: liveins: $xmm0
+ ; X86-NEXT: {{ $}}
+ ; X86-NEXT: [[WIDE:%[0-9]+]]:_(<4 x s32>) = COPY $xmm0
+ ; X86-NEXT: [[V:%[0-9]+]]:_(<2 x s32>), {{%[0-9]+}}:_(<2 x s32>) = G_UNMERGE_VALUES [[WIDE]](<4 x s32>)
+ ; X86-NEXT: [[I:%[0-9]+]]:_(s64) = G_BITCAST [[V]](<2 x s32>)
+ ; X86-NEXT: [[P:%[0-9]+]]:_(p0) = G_INTTOPTR [[I]](s64)
+ ; X86-NEXT: $rax = COPY [[P]](p0)
+ ; X86-NEXT: RET 0, implicit $rax
+ %p = bitcast <2 x b32> %v to ptr
+ ret ptr %p
+}
+
+; Destination is a vector of bytes, not an LLVM integer type: G_PTRTOINT to
+; s64 then G_BITCAST.
+define <2 x b32> @irtranslator_bitcast_ptr_to_v2b32(ptr %p) {
+ ; AARCH64-LABEL: name: irtranslator_bitcast_ptr_to_v2b32
+ ; AARCH64: bb.1 (%ir-block.0):
+ ; AARCH64-NEXT: liveins: $x0
+ ; AARCH64-NEXT: {{ $}}
+ ; AARCH64-NEXT: [[P:%[0-9]+]]:_(p0) = COPY $x0
+ ; AARCH64-NEXT: [[I:%[0-9]+]]:_(s64) = G_PTRTOINT [[P]](p0)
+ ; AARCH64-NEXT: [[V:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[I]](s64)
+ ; AARCH64-NEXT: $d0 = COPY [[V]](<2 x i32>)
+ ; AARCH64-NEXT: RET_ReallyLR implicit $d0
+ ;
+ ; AMDGPU-LABEL: name: irtranslator_bitcast_ptr_to_v2b32
+ ; AMDGPU: bb.1 (%ir-block.0):
+ ; AMDGPU-NEXT: liveins: $vgpr0, $vgpr1
+ ; AMDGPU-NEXT: {{ $}}
+ ; AMDGPU-NEXT: [[E0:%[0-9]+]]:_(s32) = COPY $vgpr0
+ ; AMDGPU-NEXT: [[E1:%[0-9]+]]:_(s32) = COPY $vgpr1
+ ; AMDGPU-NEXT: [[P:%[0-9]+]]:_(p0) = G_MERGE_VALUES [[E0]](s32), [[E1]](s32)
+ ; AMDGPU-NEXT: [[I:%[0-9]+]]:_(s64) = G_PTRTOINT [[P]](p0)
+ ; AMDGPU-NEXT: [[V:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[I]](s64)
+ ; AMDGPU-NEXT: [[U0:%[0-9]+]]:_(s32), [[U1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[V]](<2 x s32>)
+ ; AMDGPU-NEXT: $vgpr0 = COPY [[U0]](s32)
+ ; AMDGPU-NEXT: $vgpr1 = COPY [[U1]](s32)
+ ; AMDGPU-NEXT: SI_RETURN implicit $vgpr0, implicit $vgpr1
+ ;
+ ; X86-LABEL: name: irtranslator_bitcast_ptr_to_v2b32
+ ; X86: bb.1 (%ir-block.0):
+ ; X86-NEXT: liveins: $rdi
+ ; X86-NEXT: {{ $}}
+ ; X86-NEXT: [[P:%[0-9]+]]:_(p0) = COPY $rdi
+ ; X86-NEXT: [[I:%[0-9]+]]:_(s64) = G_PTRTOINT [[P]](p0)
+ ; X86-NEXT: [[V:%[0-9]+]]:_(<2 x s32>) = G_BITCAST [[I]](s64)
+ ; X86-NEXT: [[E0:%[0-9]+]]:_(s32), [[E1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[V]](<2 x s32>)
+ ; X86-NEXT: [[Z:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
+ ; X86-NEXT: [[WIDE:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[E0]](s32), [[E1]](s32), [[Z]](s32), [[Z]](s32)
+ ; X86-NEXT: $xmm0 = COPY [[WIDE]](<4 x s32>)
+ ; X86-NEXT: RET 0, implicit $xmm0
+ %v = bitcast ptr %p to <2 x b32>
+ ret <2 x b32> %v
+}
+
;;
;; The "bytecast" surface: bitcast is the only IR cast that may produce or
;; consume a byte type. The cases below correspond to LangRef's
More information about the llvm-commits
mailing list