[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