[llvm] [InferAddressSpaces] apply InferAddressSpaces to inttoptr-zext-ptrtoint address expression. (PR #79108)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 29 23:35:51 PST 2024


https://github.com/yujc9 updated https://github.com/llvm/llvm-project/pull/79108

>From b1de8dde1e9db2e4253733b841c8fd21999acd5d Mon Sep 17 00:00:00 2001
From: "Jincheng.Yu" <yujc.astro at outlook.com>
Date: Tue, 30 Jan 2024 15:34:46 +0800
Subject: [PATCH] [InferAddressSpaces] apply InferAddressSpaces to
 inttoptr-zext-ptrtoint address expression.

Signed-off-by: Jincheng.Yu <yujc.astro at outlook.com>
---
 .../Transforms/Scalar/InferAddressSpaces.cpp  | 110 +++++++++++-------
 .../zext-ptrint-conversion.ll                 |  62 ++++++++++
 2 files changed, 133 insertions(+), 39 deletions(-)
 create mode 100644 llvm/test/Transforms/InferAddressSpaces/zext-ptrint-conversion.ll

diff --git a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
index 1bf50d79e5331..f904c0e2eba19 100644
--- a/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAddressSpaces.cpp
@@ -266,38 +266,68 @@ static Type *getPtrOrVecOfPtrsWithNewAS(Type *Ty, unsigned NewAddrSpace) {
   return Ty->getWithNewType(NPT);
 }
 
-// Check whether that's no-op pointer bicast using a pair of
-// `ptrtoint`/`inttoptr` due to the missing no-op pointer bitcast over
-// different address spaces.
-static bool isNoopPtrIntCastPair(const Operator *I2P, const DataLayout &DL,
-                                 const TargetTransformInfo *TTI) {
+// Return valid IntToPtr Operator if it's a no-op pointer bitcast using a pair
+// of `ptrtoint`/`inttoptr` or a pointer bitcast using
+// `ptrtoint`-`zext`-`inttoptr` over different address spaces.
+static Operator *getNoopOrExtPtrIntCastPair(const Operator *I2P,
+                                            const DataLayout &DL,
+                                            const TargetTransformInfo *TTI) {
   assert(I2P->getOpcode() == Instruction::IntToPtr);
-  auto *P2I = dyn_cast<Operator>(I2P->getOperand(0));
-  if (!P2I || P2I->getOpcode() != Instruction::PtrToInt)
-    return false;
-  // Check it's really safe to treat that pair of `ptrtoint`/`inttoptr` as a
-  // no-op cast. Besides checking both of them are no-op casts, as the
-  // reinterpreted pointer may be used in other pointer arithmetic, we also
-  // need to double-check that through the target-specific hook. That ensures
-  // the underlying target also agrees that's a no-op address space cast and
-  // pointer bits are preserved.
-  // The current IR spec doesn't have clear rules on address space casts,
-  // especially a clear definition for pointer bits in non-default address
-  // spaces. It would be undefined if that pointer is dereferenced after an
-  // invalid reinterpret cast. Also, due to the unclearness for the meaning of
-  // bits in non-default address spaces in the current spec, the pointer
-  // arithmetic may also be undefined after invalid pointer reinterpret cast.
-  // However, as we confirm through the target hooks that it's a no-op
-  // addrspacecast, it doesn't matter since the bits should be the same.
-  unsigned P2IOp0AS = P2I->getOperand(0)->getType()->getPointerAddressSpace();
-  unsigned I2PAS = I2P->getType()->getPointerAddressSpace();
-  return CastInst::isNoopCast(Instruction::CastOps(I2P->getOpcode()),
-                              I2P->getOperand(0)->getType(), I2P->getType(),
-                              DL) &&
-         CastInst::isNoopCast(Instruction::CastOps(P2I->getOpcode()),
-                              P2I->getOperand(0)->getType(), P2I->getType(),
-                              DL) &&
-         (P2IOp0AS == I2PAS || TTI->isNoopAddrSpaceCast(P2IOp0AS, I2PAS));
+  auto *Op = dyn_cast<Operator>(I2P->getOperand(0));
+  bool validAddrSpaceCast = false;
+  Operator *P2I = nullptr;
+  switch (Op->getOpcode()) {
+  case Instruction::PtrToInt: {
+    // Check it's really safe to treat that pair of `ptrtoint`/`inttoptr` as a
+    // no-op cast. We need to double-check that through the target-specific
+    // hook. That ensures the underlying target also agrees that's a no-op
+    // address space cast and pointer bits are preserved. The current IR spec
+    // doesn't have clear rules on address space casts, especially a clear
+    // definition for pointer bits in non-default address spaces. It would be
+    // undefined if that pointer is dereferenced after an invalid reinterpret
+    // cast. Also, due to the unclearness for the meaning of bits in non-default
+    // address spaces in the current spec, the pointer arithmetic may also be
+    // undefined after invalid pointer reinterpret cast. However, as we confirm
+    // through the target hooks that it's a no-op addrspacecast, it doesn't
+    // matter since the bits should be the same.
+    P2I = Op;
+    unsigned P2IOp0AS = P2I->getOperand(0)->getType()->getPointerAddressSpace();
+    unsigned I2PAS = I2P->getType()->getPointerAddressSpace();
+    validAddrSpaceCast =
+        (P2IOp0AS == I2PAS || TTI->isNoopAddrSpaceCast(P2IOp0AS, I2PAS));
+    break;
+  }
+  case Instruction::ZExt: {
+    // check src address pointer and dst address pointer should be different
+    // address space and different size
+    P2I = dyn_cast<Operator>(Op->getOperand(0));
+    if (P2I && P2I->getOpcode() == Instruction::PtrToInt) {
+      unsigned P2IOp0AS =
+          P2I->getOperand(0)->getType()->getPointerAddressSpace();
+      unsigned I2PAS = I2P->getType()->getPointerAddressSpace();
+      unsigned P2IOp0SizeInBits =
+          DL.getIntPtrType(P2I->getOperand(0)->getType())
+              ->getScalarSizeInBits();
+      unsigned I2PSizeInBits =
+          DL.getIntPtrType(I2P->getType())->getScalarSizeInBits();
+      validAddrSpaceCast =
+          (P2IOp0AS != I2PAS && P2IOp0SizeInBits < I2PSizeInBits);
+    }
+    break;
+  }
+  default:
+    break;
+  }
+  // We also need to check both `inttoptr` and `ptrtoint` are no-op casts, as
+  // the reinterpreted pointer may be used in other pointer arithmetic.
+  if (validAddrSpaceCast &&
+      CastInst::isNoopCast(Instruction::CastOps(I2P->getOpcode()),
+                           I2P->getOperand(0)->getType(), I2P->getType(), DL) &&
+      CastInst::isNoopCast(Instruction::CastOps(P2I->getOpcode()),
+                           P2I->getOperand(0)->getType(), P2I->getType(), DL)) {
+    return P2I;
+  }
+  return nullptr;
 }
 
 // Returns true if V is an address expression.
@@ -324,7 +354,7 @@ static bool isAddressExpression(const Value &V, const DataLayout &DL,
     return II && II->getIntrinsicID() == Intrinsic::ptrmask;
   }
   case Instruction::IntToPtr:
-    return isNoopPtrIntCastPair(Op, DL, TTI);
+    return getNoopOrExtPtrIntCastPair(Op, DL, TTI) != nullptr;
   default:
     // That value is an address expression if it has an assumed address space.
     return TTI->getAssumedAddrSpace(&V) != UninitializedAddressSpace;
@@ -356,8 +386,8 @@ getPointerOperands(const Value &V, const DataLayout &DL,
     return {II.getArgOperand(0)};
   }
   case Instruction::IntToPtr: {
-    assert(isNoopPtrIntCastPair(&Op, DL, TTI));
-    auto *P2I = cast<Operator>(Op.getOperand(0));
+    assert(getNoopOrExtPtrIntCastPair(&Op, DL, TTI));
+    Operator *P2I = getNoopOrExtPtrIntCastPair(&Op, DL, TTI);
     return {P2I->getOperand(0)};
   }
   default:
@@ -521,8 +551,9 @@ InferAddressSpacesImpl::collectFlatAddressExpressions(Function &F) const {
     } else if (auto *ASC = dyn_cast<AddrSpaceCastInst>(&I)) {
       PushPtrOperand(ASC->getPointerOperand());
     } else if (auto *I2P = dyn_cast<IntToPtrInst>(&I)) {
-      if (isNoopPtrIntCastPair(cast<Operator>(I2P), *DL, TTI))
-        PushPtrOperand(cast<Operator>(I2P->getOperand(0))->getOperand(0));
+      Operator *P2I = getNoopOrExtPtrIntCastPair(cast<Operator>(I2P), *DL, TTI);
+      if (P2I)
+        PushPtrOperand(cast<Operator>(P2I)->getOperand(0));
     } else if (auto *RI = dyn_cast<ReturnInst>(&I)) {
       if (auto *RV = RI->getReturnValue();
           RV && RV->getType()->isPtrOrPtrVectorTy())
@@ -683,8 +714,9 @@ Value *InferAddressSpacesImpl::cloneInstructionWithNewAddressSpace(
     return SelectInst::Create(I->getOperand(0), NewPointerOperands[1],
                               NewPointerOperands[2], "", nullptr, I);
   case Instruction::IntToPtr: {
-    assert(isNoopPtrIntCastPair(cast<Operator>(I), *DL, TTI));
-    Value *Src = cast<Operator>(I->getOperand(0))->getOperand(0);
+    assert(getNoopOrExtPtrIntCastPair(cast<Operator>(I), *DL, TTI));
+    Operator *P2I = getNoopOrExtPtrIntCastPair(cast<Operator>(I), *DL, TTI);
+    Value *Src = cast<Operator>(P2I)->getOperand(0);
     if (Src->getType() == NewPtrType)
       return Src;
 
@@ -726,7 +758,7 @@ static Value *cloneConstantExprWithNewAddressSpace(
   }
 
   if (CE->getOpcode() == Instruction::IntToPtr) {
-    assert(isNoopPtrIntCastPair(cast<Operator>(CE), *DL, TTI));
+    assert(getNoopOrExtPtrIntCastPair(cast<Operator>(CE), *DL, TTI));
     Constant *Src = cast<ConstantExpr>(CE->getOperand(0))->getOperand(0);
     assert(Src->getType()->getPointerAddressSpace() == NewAddrSpace);
     return ConstantExpr::getBitCast(Src, TargetType);
diff --git a/llvm/test/Transforms/InferAddressSpaces/zext-ptrint-conversion.ll b/llvm/test/Transforms/InferAddressSpaces/zext-ptrint-conversion.ll
new file mode 100644
index 0000000000000..07b8eb7a2e34b
--- /dev/null
+++ b/llvm/test/Transforms/InferAddressSpaces/zext-ptrint-conversion.ll
@@ -0,0 +1,62 @@
+; RUN: opt -S -o - -passes=infer-address-spaces -assume-default-is-flat-addrspace %s | FileCheck %s
+
+target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-ni:7:8"
+
+; CHECK-LABEL: @zext_ptrint_conversion(
+; CHECK-NEXT: store i32 0, ptr addrspace(3) %{{.*}}
+; CHECK-NEXT: ret void
+define void @zext_ptrint_conversion(ptr addrspace(3) %x) {
+  %tmp0 = ptrtoint ptr addrspace(3) %x to i32
+  %tmp1 = zext i32 %tmp0 to i64
+  %tmp2 = inttoptr i64 %tmp1 to ptr
+  store i32 0, ptr %tmp2
+  ret void
+}
+
+; CHECK-LABEL: @zext_ptrint_conversion2(
+; CHECK-NEXT: addrspacecast ptr addrspace(3) %{{.*}} to ptr
+; CHECK-NEXT: ret ptr %{{.*}}
+define ptr @zext_ptrint_conversion2(ptr addrspace(3) %x) {
+  %tmp0 = ptrtoint ptr addrspace(3) %x to i32
+  %tmp1 = zext i32 %tmp0 to i64
+  %tmp2 = inttoptr i64 %tmp1 to ptr
+  ret ptr %tmp2
+}
+
+; CHECK-LABEL: @zext_ptrint_conversion3(
+; CHECK-NEXT: addrspacecast <2 x ptr addrspace(3)> %{{.*}} to <2 x ptr>
+; CHECK-NEXT: ret <2 x ptr> %{{.*}}
+define <2 x ptr> @zext_ptrint_conversion3(<2 x ptr addrspace(3)> %x) {
+  %tmp0 = ptrtoint <2 x ptr addrspace(3)> %x to <2 x i32>
+  %tmp1 = zext <2 x i32> %tmp0 to <2 x i64>
+  %tmp2 = inttoptr <2 x i64> %tmp1 to <2 x ptr>
+  ret <2 x ptr> %tmp2
+}
+
+; CHECK-LABEL: @non_zext_ptrint_conversion(
+; CHECK-NEXT: ptrtoint ptr addrspace(3) %{{.*}} to i16
+; CHECK-NEXT: zext i16 %{{.*}} to i64
+; CHECK-NEXT: inttoptr i64 %{{.*}} to ptr
+; CHECK-NEXT: store i32 0, ptr %{{.*}}
+; CHECK-NEXT: ret void
+define void @non_zext_ptrint_conversion(ptr addrspace(3) %x) {
+  %tmp1 = ptrtoint ptr addrspace(3) %x to i16
+  %tmp2 = zext i16 %tmp1 to i64
+  %tmp3 = inttoptr i64 %tmp2 to ptr
+  store i32 0, ptr %tmp3
+  ret void
+}
+
+; CHECK-LABEL: @non_zext_ptrint_conversion2(
+; CHECK-NEXT: ptrtoint ptr addrspace(3) %{{.*}} to i32
+; CHECK-NEXT: zext i32 %{{.*}} to i128
+; CHECK-NEXT: inttoptr i128 %{{.*}} to ptr
+; CHECK-NEXT: store i32 0, ptr %{{.*}}
+; CHECK-NEXT: ret void
+define void @non_zext_ptrint_conversion2(ptr addrspace(3) %x) {
+  %tmp1 = ptrtoint ptr addrspace(3) %x to i32
+  %tmp2 = zext i32 %tmp1 to i128
+  %tmp3 = inttoptr i128 %tmp2 to ptr
+  store i32 0, ptr %tmp3
+  ret void
+}



More information about the llvm-commits mailing list