fhahn created this revision.
fhahn added reviewers: jfb, rsmith, rjmccall, efriedma, hfinkel.
Herald added subscribers: llvm-commits, dexonsmith, hiraditya.
Herald added projects: clang, LLVM.

I am not sure if this is the best way to do the matching and would
appreciate any pointers for improving it.

Also, I am not sure if any of the available targets limits the pointer
size, e.g. to something like 56 bit pointers.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D64128

Files:
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/test/CodeGen/x86-ptrmask.c
  llvm/include/llvm/IR/DataLayout.h
  llvm/lib/IR/DataLayout.cpp

Index: llvm/lib/IR/DataLayout.cpp
===================================================================
--- llvm/lib/IR/DataLayout.cpp
+++ llvm/lib/IR/DataLayout.cpp
@@ -633,6 +633,14 @@
   return I->ABIAlign;
 }
 
+unsigned DataLayout::getMinPointerABIAlignment() const {
+  unsigned MaxABIAlign = std::numeric_limits<unsigned>::max();
+  for (auto &P : Pointers)
+    MaxABIAlign = std::min(MaxABIAlign, P.ABIAlign);
+
+  return MaxABIAlign;
+}
+
 unsigned DataLayout::getPointerPrefAlignment(unsigned AS) const {
   PointersTy::const_iterator I = findPointerLowerBound(AS);
   if (I == Pointers.end() || I->AddressSpace != AS) {
Index: llvm/include/llvm/IR/DataLayout.h
===================================================================
--- llvm/include/llvm/IR/DataLayout.h
+++ llvm/include/llvm/IR/DataLayout.h
@@ -346,6 +346,8 @@
   /// Layout pointer alignment
   unsigned getPointerABIAlignment(unsigned AS) const;
 
+  unsigned getMinPointerABIAlignment() const;
+
   /// Return target's alignment for stack-based pointers
   /// FIXME: The defaults need to be removed once all of
   /// the backends/clients are updated.
Index: clang/test/CodeGen/x86-ptrmask.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/x86-ptrmask.c
@@ -0,0 +1,59 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -triple x86_64-macos-apple -emit-llvm %s -o - | FileCheck %s
+
+#include <stdint.h>
+
+// CHECK-LABEL: define i8* @strip_2_low_bits(i8* %p) #0 {
+// CHECK-NEXT:   entry:
+// CHECK-NEXT:     %p.addr = alloca i8*, align 8
+// CHECK-NEXT:     store i8* %p, i8** %p.addr, align 8
+// CHECK-NEXT:     %0 = load i8*, i8** %p.addr, align 8
+// CHECK-NEXT:     %1 = call i8* @llvm.ptrmask.p0i8.p0i8.i64(i8* %0, i64 -4)
+// CHECK-NEXT:     ret i8* %1
+// CHECK-NEXT:   }
+char *strip_2_low_bits(char *p) {
+  return (char *)(((intptr_t)p) & ~((1 << 2) - 1));
+}
+
+// CHECK-LABEL: define i8* @strip_3_low_bits(i8* %p) #0 {
+// CHECK-NEXT:   entry:
+// CHECK-NEXT:     %p.addr = alloca i8*, align 8
+// CHECK-NEXT:     store i8* %p, i8** %p.addr, align 8
+// CHECK-NEXT:     %0 = load i8*, i8** %p.addr, align 8
+// CHECK-NEXT:     %1 = call i8* @llvm.ptrmask.p0i8.p0i8.i64(i8* %0, i64 -8)
+// CHECK-NEXT:     ret i8* %1
+// CHECK-NEXT:   }
+char *strip_3_low_bits(char *p) {
+  return (char *)(((intptr_t)p) & ~((1 << 3) - 1));
+}
+
+// CHECK-LABEL: define i8* @strip_3_low_bits_const_add(i8* %p) #0 {
+// CHECK-NEXT:   entry:
+// CHECK-NEXT:     %p.addr = alloca i8*, align 8
+// CHECK-NEXT:     store i8* %p, i8** %p.addr, align 8
+// CHECK-NEXT:     %0 = load i8*, i8** %p.addr, align 8
+// CHECK-NEXT:     %1 = call i8* @llvm.ptrmask.p0i8.p0i8.i64(i8* %0, i64 -8)
+// CHECK-NEXT:     ret i8* %1
+// CHECK-NEXT:   }
+char *strip_3_low_bits_const_add(char *p) {
+  return (char *)(((intptr_t)(p)) & ~((1 << (1 + 2)) - 1));
+}
+
+// Just make sure we do not emit ptrmask calls when it is not valid.
+// CHECK-LABEL: define i8* @strip_4_low_bits(i8* %p) #0 {
+// CHECK-NOT: @llvm.ptrmask
+char *strip_4_low_bits(char *p) {
+  return (char *)(((intptr_t)p) & ~((1 << 4) - 1));
+}
+
+// CHECK-LABEL: define i8* @strip_4_low_bits_const_add(i8* %p) #0 {
+// CHECK-NOT: @llvm.ptrmask
+char *strip_4_low_bits_const_add(char *p) {
+  return (char *)(((intptr_t)p) & ~((1 << (2+2)) - 1));
+}
+
+// CHECK-LABEL: define i8* @strip_variable(i8* %p, i64 %m) #0 {
+// CHECK-NOT: @llvm.ptrmask
+char *strip_variable(char *p, intptr_t m) {
+  return (char *)(((intptr_t)p) & m);
+}
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -2005,6 +2005,55 @@
   return true;
 }
 
+// Try to generate llvm.ptrmask instead of inttoptr(and(ptrtoint, C)) if
+// possible.
+static llvm::Value *tryToGeneratePtrMask(Expr *E, QualType &DestTy,
+                                         CodeGenFunction &CGF,
+                                         ScalarExprEmitter *Emitter,
+                                         CGBuilderTy &Builder) {
+  auto peelParens = [](Expr *E) {
+    auto *PE = dyn_cast<ParenExpr>(E);
+    if (!PE)
+      return E;
+    return PE->getSubExpr();
+  };
+
+  auto *BO = dyn_cast<BinaryOperator>(peelParens(E));
+  if (!BO || BO->getOpcode() != BinaryOperatorKind::BO_And)
+    return nullptr;
+
+  // Check if the RHS of the AND is a constant that only masks away high and/or
+  // low bits that must be zero when accessing it, because of ABI alignment
+  // requirements or a restriction of the meaningful bits of a pointer
+  // through the data layout.
+  llvm::APSInt CV;
+  ASTContext &Ctx = CGF.getContext();
+  auto &DL = CGF.CGM.getDataLayout();
+  uint64_t AllOnes = ~((uint64_t)0);
+  uint64_t MeaningfulPtrBits =
+      (AllOnes >> (64 - DL.getMaxPointerSizeInBits())) &
+      (AllOnes << llvm::Log2_64(DL.getMinPointerABIAlignment()));
+  if (!BO->getRHS()->isIntegerConstantExpr(CV, Ctx) ||
+      (CV.getZExtValue() & MeaningfulPtrBits) != MeaningfulPtrBits)
+    return nullptr;
+
+  auto *CE = dyn_cast<CastExpr>(peelParens(BO->getLHS()));
+  if (!CE || CE->getCastKind() != CK_PointerToIntegral)
+    return nullptr;
+
+  // We can generate a ptrmask call. Now visit the relevant operands and emit
+  // the call.
+  Value *Ptr = Emitter->Visit(const_cast<Expr *>(CE->getSubExpr()));
+  Value *Const = Emitter->Visit(const_cast<Expr *>(BO->getRHS()));
+  auto *PtrMask = Builder.CreateIntrinsic(
+      llvm::Intrinsic::ptrmask,
+      {Ptr->getType(), Ptr->getType(), Const->getType()}, {Ptr, Const});
+  auto DestLLVMTy = Emitter->ConvertType(DestTy);
+  if (DestLLVMTy == Ptr->getType())
+    return PtrMask;
+  return Builder.CreateBitCast(PtrMask, DestLLVMTy);
+};
+
 // VisitCastExpr - Emit code for an explicit or implicit cast.  Implicit casts
 // have to handle a more broad range of conversions than explicit casts, as they
 // handle things like function to ptr-to-function decay etc.
@@ -2212,8 +2261,12 @@
     return Visit(const_cast<Expr*>(E));
 
   case CK_IntegralToPointer: {
-    Value *Src = Visit(const_cast<Expr*>(E));
+    // Try to generate llvm.ptrmask instead of inttoptr(and(ptrtoint, C)) if
+    // possible.
+    if (Value *PtrMask = tryToGeneratePtrMask(E, DestTy, CGF, this, Builder))
+      return PtrMask;
 
+    Value *Src = Visit(const_cast<Expr *>(E));
     // First, convert to the correct width so that we control the kind of
     // extension.
     auto DestLLVMTy = ConvertType(DestTy);
@@ -3248,11 +3301,10 @@
   //   The index is not pointer-sized.
   //   The pointer type is not byte-sized.
   //
-  if (BinaryOperator::isNullPointerArithmeticExtension(CGF.getContext(),
-                                                       op.Opcode,
-                                                       expr->getLHS(),
-                                                       expr->getRHS()))
+  if (BinaryOperator::isNullPointerArithmeticExtension(
+          CGF.getContext(), op.Opcode, expr->getLHS(), expr->getRHS())) {
     return CGF.Builder.CreateIntToPtr(index, pointer->getType());
+  }
 
   if (width != DL.getTypeSizeInBits(PtrTy)) {
     // Zero-extend or sign-extend the pointer value according to
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to