Commit 89c2e733 authored by Johannes Doerfert's avatar Johannes Doerfert
Browse files

[Attributor] Pointer privatization attribute (argument promotion)

A pointer is privatizeable if it can be replaced by a new, private one.
Privatizing pointer reduces the use count, interaction between unrelated
code parts. This is a first step towards replacing argument promotion.
While we can already handle recursion (unlike argument promotion!) we
are restricted to stack allocations for now because we do not analyze
the uses in the callee.

Reviewed By: uenoku

Differential Revision: https://reviews.llvm.org/D68852
parent f0654875
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include "llvm/IR/PassManager.h"

namespace llvm {
class TargetTransformInfo;

/// Argument promotion pass.
///
@@ -26,6 +27,17 @@ class ArgumentPromotionPass : public PassInfoMixin<ArgumentPromotionPass> {
public:
  ArgumentPromotionPass(unsigned MaxElements = 3u) : MaxElements(MaxElements) {}

  /// Check if callers and the callee \p F agree how promoted arguments would be
  /// passed. The ones that they do not agree on are eliminated from the sets but
  /// the return value has to be observed as well.
  static bool areFunctionArgsABICompatible(
      const Function &F, const TargetTransformInfo &TTI,
      SmallPtrSetImpl<Argument *> &ArgsToPromote,
      SmallPtrSetImpl<Argument *> &ByValArgsToTransform);

  /// Checks if a type could have padding bytes.
  static bool isDenselyPacked(Type *type, const DataLayout &DL);

  PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
                        LazyCallGraph &CG, CGSCCUpdateResult &UR);
};
+50 −0
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Analysis/MustExecute.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/PassManager.h"
@@ -951,6 +952,16 @@ struct Attributor {
    friend struct Attributor;
  };

  /// Check if we can rewrite a function signature.
  ///
  /// The argument \p Arg is replaced with new ones defined by the number,
  /// order, and types in \p ReplacementTypes.
  ///
  /// \returns True, if the replacement can be registered, via
  /// registerFunctionSignatureRewrite, false otherwise.
  bool isValidFunctionSignatureRewrite(Argument &Arg,
                                       ArrayRef<Type *> ReplacementTypes);

  /// Register a rewrite for a function signature.
  ///
  /// The argument \p Arg is replaced with new ones defined by the number,
@@ -2402,6 +2413,45 @@ struct AAHeapToStack : public StateWrapper<BooleanState, AbstractAttribute>,
  static const char ID;
};

/// An abstract interface for privatizability.
///
/// A pointer is privatizable if it can be replaced by a new, private one.
/// Privatizing pointer reduces the use count, interaction between unrelated
/// code parts.
///
/// In order for a pointer to be privatizable its value cannot be observed
/// (=nocapture), it is (for now) not written (=readonly & noalias), we know
/// what values are necessary to make the private copy look like the original
/// one, and the values we need can be loaded (=dereferenceable).
struct AAPrivatizablePtr : public StateWrapper<BooleanState, AbstractAttribute>,
                           public IRPosition {
  AAPrivatizablePtr(const IRPosition &IRP) : IRPosition(IRP) {}

  /// Returns true if pointer privatization is assumed to be possible.
  bool isAssumedPrivatizablePtr() const { return getAssumed(); }

  /// Returns true if pointer privatization is known to be possible.
  bool isKnownPrivatizablePtr() const { return getKnown(); }

  /// Return the type we can choose for a private copy of the underlying
  /// value. None means it is not clear yet, nullptr means there is none.
  virtual Optional<Type *> getPrivatizableType() const = 0;

  /// Return an IR position, see struct IRPosition.
  ///
  ///{
  IRPosition &getIRPosition() { return *this; }
  const IRPosition &getIRPosition() const { return *this; }
  ///}

  /// Create an abstract attribute view for the position \p IRP.
  static AAPrivatizablePtr &createForPosition(const IRPosition &IRP,
                                              Attributor &A);

  /// Unique ID (due to the unique address)
  static const char ID;
};

/// An abstract interface for all memory related attributes.
struct AAMemoryBehavior
    : public IRAttribute<
+9 −8
Original line number Diff line number Diff line
@@ -774,8 +774,7 @@ static bool isSafeToPromoteArgument(Argument *Arg, Type *ByValTy, AAResults &AAR
  return true;
}

/// Checks if a type could have padding bytes.
static bool isDenselyPacked(Type *type, const DataLayout &DL) {
bool ArgumentPromotionPass::isDenselyPacked(Type *type, const DataLayout &DL) {
  // There is no size information, so be conservative.
  if (!type->isSized())
    return false;
@@ -844,12 +843,14 @@ static bool canPaddingBeAccessed(Argument *arg) {
  return false;
}

static bool areFunctionArgsABICompatible(
bool ArgumentPromotionPass::areFunctionArgsABICompatible(
    const Function &F, const TargetTransformInfo &TTI,
    SmallPtrSetImpl<Argument *> &ArgsToPromote,
    SmallPtrSetImpl<Argument *> &ByValArgsToTransform) {
  for (const Use &U : F.uses()) {
    CallSite CS(U.getUser());
    if (!CS)
      return false;
    const Function *Caller = CS.getCaller();
    const Function *Callee = CS.getCalledFunction();
    if (!TTI.areFunctionArgsABICompatible(Caller, Callee, ArgsToPromote) ||
@@ -951,9 +952,9 @@ promoteArguments(Function *F, function_ref<AAResults &(Function &F)> AARGetter,
    // If this is a byval argument, and if the aggregate type is small, just
    // pass the elements, which is always safe, if the passed value is densely
    // packed or if we can prove the padding bytes are never accessed.
    bool isSafeToPromote =
        PtrArg->hasByValAttr() &&
        (isDenselyPacked(AgTy, DL) || !canPaddingBeAccessed(PtrArg));
    bool isSafeToPromote = PtrArg->hasByValAttr() &&
                           (ArgumentPromotionPass::isDenselyPacked(AgTy, DL) ||
                            !canPaddingBeAccessed(PtrArg));
    if (isSafeToPromote) {
      if (StructType *STy = dyn_cast<StructType>(AgTy)) {
        if (MaxElements > 0 && STy->getNumElements() > MaxElements) {
@@ -1011,8 +1012,8 @@ promoteArguments(Function *F, function_ref<AAResults &(Function &F)> AARGetter,
  if (ArgsToPromote.empty() && ByValArgsToTransform.empty())
    return nullptr;

  if (!areFunctionArgsABICompatible(*F, TTI, ArgsToPromote,
                                    ByValArgsToTransform))
  if (!ArgumentPromotionPass::areFunctionArgsABICompatible(
          *F, TTI, ArgsToPromote, ByValArgsToTransform))
    return nullptr;

  return doPromotion(F, ArgsToPromote, ByValArgsToTransform, ReplaceCallSite);
+669 −6

File changed.

Preview size limit exceeded, changes collapsed.

+6 −3
Original line number Diff line number Diff line
@@ -3,9 +3,11 @@

define internal i32 @deref(i32* %x) nounwind {
; CHECK-LABEL: define {{[^@]+}}@deref
; CHECK-SAME: (i32* noalias nocapture nofree nonnull readonly align 4 dereferenceable(4) [[X:%.*]])
; CHECK-SAME: (i32 [[TMP0:%.*]])
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, i32* [[X]], align 4
; CHECK-NEXT:    [[X_PRIV:%.*]] = alloca i32
; CHECK-NEXT:    store i32 [[TMP0]], i32* [[X_PRIV]]
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, i32* [[X_PRIV]], align 4
; CHECK-NEXT:    ret i32 [[TMP2]]
;
entry:
@@ -19,7 +21,8 @@ define i32 @f(i32 %x) {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i32
; CHECK-NEXT:    store i32 [[X]], i32* [[X_ADDR]], align 4
; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @deref(i32* noalias nocapture nofree nonnull readonly align 4 dereferenceable(4) [[X_ADDR]])
; CHECK-NEXT:    [[TMP0:%.*]] = load i32, i32* [[X_ADDR]], align 1
; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @deref(i32 [[TMP0]])
; CHECK-NEXT:    ret i32 [[TMP1]]
;
entry:
Loading