Commit b2c76002 authored by Johannes Doerfert's avatar Johannes Doerfert
Browse files

[Attributor] Ignore uses if a value is simplified

If we have a replacement for a value, via AAValueSimplify, the original
value will lose all its uses. Thus, as long as a value is simplified we
can skip the uses in checkForAllUses, given that these uses are
transitive uses for the simplified version and will therefore affect the
simplified version as necessary.

Since this allowed us to remove calls without side-effects and a known
return value, we need to make sure not to eliminate `musttail` calls.
Those we keep around, or later remove the entire `musttail` call chain.
parent 86509e8c
Loading
Loading
Loading
Loading
+35 −3
Original line number Diff line number Diff line
@@ -2916,6 +2916,12 @@ struct AAIsDeadFloating : public AAIsDeadValueImpl {
    if (V.use_empty())
      return ChangeStatus::UNCHANGED;

    bool UsedAssumedInformation = false;
    Optional<ConstantInt *> CI =
        getAssumedConstant(A, V, *this, UsedAssumedInformation);
    if (CI.hasValue() && CI.getValue())
      return ChangeStatus::UNCHANGED;

    UndefValue &UV = *UndefValue::get(V.getType());
    bool AnyChange = A.changeValueAfterManifest(V, UV);
    return AnyChange ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
@@ -3092,6 +3098,9 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl {
    UndefValue &UV = *UndefValue::get(getAssociatedFunction()->getReturnType());
    auto RetInstPred = [&](Instruction &I) {
      ReturnInst &RI = cast<ReturnInst>(I);
      if (auto *CI = dyn_cast<CallInst>(RI.getReturnValue()))
        if (CI->isMustTailCall())
          return true;
      if (!isa<UndefValue>(RI.getReturnValue()))
        AnyChange |= A.changeUseAfterManifest(RI.getOperandUse(0), UV);
      return true;
@@ -4730,6 +4739,14 @@ struct AAValueSimplifyCallSiteReturned : AAValueSimplifyReturned {
  AAValueSimplifyCallSiteReturned(const IRPosition &IRP)
      : AAValueSimplifyReturned(IRP) {}

  /// See AbstractAttribute::manifest(...).
  ChangeStatus manifest(Attributor &A) override {
    if (auto *CI = dyn_cast<CallInst>(&getAssociatedValue()))
      if (CI->isMustTailCall())
        return ChangeStatus::UNCHANGED;
    return AAValueSimplifyReturned::manifest(A);
  }

  void trackStatistics() const override {
    STATS_DECLTRACK_CSRET_ATTR(value_simplify)
  }
@@ -6557,6 +6574,24 @@ bool Attributor::isAssumedDead(const AbstractAttribute &AA,
bool Attributor::checkForAllUses(
    const function_ref<bool(const Use &, bool &)> &Pred,
    const AbstractAttribute &QueryingAA, const Value &V) {

  // Check the trivial case first as it catches void values.
  if (V.use_empty())
    return true;

  // If the value is replaced by another one, for now a constant, we do not have
  // uses. Note that this requires users of `checkForAllUses` to not recurse but
  // instead use the `follow` callback argument to look at transitive users,
  // however, that should be clear from the presence of the argument.
  bool UsedAssumedInformation = false;
  Optional<ConstantInt *> CI =
      getAssumedConstant(*this, V, QueryingAA, UsedAssumedInformation);
  if (CI.hasValue() && CI.getValue()) {
    LLVM_DEBUG(dbgs() << "[Attributor] Value is simplified, uses skipped: " << V
                      << " -> " << *CI.getValue() << "\n");
    return true;
  }

  const IRPosition &IRP = QueryingAA.getIRPosition();
  SmallVector<const Use *, 16> Worklist;
  SmallPtrSet<const Use *, 16> Visited;
@@ -6567,9 +6602,6 @@ bool Attributor::checkForAllUses(
  LLVM_DEBUG(dbgs() << "[Attributor] Got " << Worklist.size()
                    << " initial uses to check\n");

  if (Worklist.empty())
    return true;

  bool AnyDead = false;
  const Function *ScopeFn = IRP.getAnchorScope();
  const auto *LivenessAA =
+2 −2
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
; because there is a load of %A in the entry block
define internal i32 @callee(i1 %C, i32* %A) {
; CHECK-LABEL: define {{[^@]+}}@callee
; CHECK-SAME: (i1 [[C:%.*]], i32* noalias nocapture nofree nonnull readonly align 536870912 dereferenceable(4) [[A:%.*]])
; CHECK-SAME: (i32* noalias nocapture nofree nonnull readonly align 536870912 dereferenceable(4) [[A:%.*]])
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[A_0:%.*]] = load i32, i32* null, align 536870912
; CHECK-NEXT:    br label [[F:%.*]]
@@ -34,7 +34,7 @@ F:

define i32 @foo() {
; CHECK-LABEL: define {{[^@]+}}@foo()
; CHECK-NEXT:    [[X:%.*]] = call i32 @callee(i1 false, i32* noalias nofree readonly align 536870912 null)
; CHECK-NEXT:    [[X:%.*]] = call i32 @callee(i32* noalias nofree readonly align 536870912 null)
; CHECK-NEXT:    ret i32 [[X]]
;
  %X = call i32 @callee(i1 false, i32* null)             ; <i32> [#uses=1]
+26 −8
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ define internal i32 @test(i32* %X, i32* %Y) {
; OLDPM-NEXT:    br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]]
; OLDPM:       live:
; OLDPM-NEXT:    store i32 0, i32* [[X]], align 4
; OLDPM-NEXT:    ret i32 0
; OLDPM-NEXT:    ret i32 undef
; OLDPM:       dead:
; OLDPM-NEXT:    unreachable
;
@@ -29,7 +29,7 @@ define internal i32 @test(i32* %X, i32* %Y) {
; NEWPM_MODULE-NEXT:    br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]]
; NEWPM_MODULE:       live:
; NEWPM_MODULE-NEXT:    store i32 0, i32* [[X]], align 4
; NEWPM_MODULE-NEXT:    ret i32 0
; NEWPM_MODULE-NEXT:    ret i32 undef
; NEWPM_MODULE:       dead:
; NEWPM_MODULE-NEXT:    unreachable
;
@@ -38,7 +38,7 @@ define internal i32 @test(i32* %X, i32* %Y) {
; NEWPM_CGSCC-NEXT:    br i1 true, label [[LIVE:%.*]], label [[DEAD:%.*]]
; NEWPM_CGSCC:       live:
; NEWPM_CGSCC-NEXT:    store i32 0, i32* [[X]], align 4
; NEWPM_CGSCC-NEXT:    ret i32 0
; NEWPM_CGSCC-NEXT:    ret i32 undef
; NEWPM_CGSCC:       dead:
; NEWPM_CGSCC-NEXT:    unreachable
;
@@ -53,11 +53,29 @@ dead:
}

define internal i32 @caller(i32* %B) {
; CHECK-LABEL: define {{[^@]+}}@caller()
; CHECK-NEXT:    [[A:%.*]] = alloca i32
; CHECK-NEXT:    store i32 1, i32* [[A]], align 4
; CHECK-NEXT:    [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]])
; CHECK-NEXT:    ret i32 0
; OLDPM_MODULE-LABEL: define {{[^@]+}}@caller()
; OLDPM_MODULE-NEXT:    [[A:%.*]] = alloca i32
; OLDPM_MODULE-NEXT:    store i32 1, i32* [[A]], align 4
; OLDPM_MODULE-NEXT:    [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]])
; OLDPM_MODULE-NEXT:    ret i32 undef
;
; OLDPM_CGSCC-LABEL: define {{[^@]+}}@caller()
; OLDPM_CGSCC-NEXT:    [[A:%.*]] = alloca i32
; OLDPM_CGSCC-NEXT:    store i32 1, i32* [[A]], align 4
; OLDPM_CGSCC-NEXT:    [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]])
; OLDPM_CGSCC-NEXT:    ret i32 0
;
; NEWPM_MODULE-LABEL: define {{[^@]+}}@caller()
; NEWPM_MODULE-NEXT:    [[A:%.*]] = alloca i32
; NEWPM_MODULE-NEXT:    store i32 1, i32* [[A]], align 4
; NEWPM_MODULE-NEXT:    [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]])
; NEWPM_MODULE-NEXT:    ret i32 undef
;
; NEWPM_CGSCC-LABEL: define {{[^@]+}}@caller()
; NEWPM_CGSCC-NEXT:    [[A:%.*]] = alloca i32
; NEWPM_CGSCC-NEXT:    store i32 1, i32* [[A]], align 4
; NEWPM_CGSCC-NEXT:    [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[A]])
; NEWPM_CGSCC-NEXT:    ret i32 0
;
  %A = alloca i32
  store i32 1, i32* %A
+2 −7
Original line number Diff line number Diff line
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s
; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s

; The original C source looked like this:
;
@@ -56,9 +56,8 @@ define internal i16 @bar(i16 %p1, i16 %p2) {
define dso_local i16 @vararg_tests(i16 %a) {
; CHECK-LABEL: define {{[^@]+}}@vararg_tests
; CHECK-SAME: (i16 [[A:%.*]])
; CHECK-NEXT:    [[CALL1:%.*]] = call i16 (i16, ...) @vararg_prop(i16 7, i16 8, i16 [[A]])
; CHECK-NEXT:    [[CALL2:%.*]] = call i16 bitcast (i16 (i16, i16, ...)* @vararg_no_prop to i16 (i16)*)(i16 7)
; CHECK-NEXT:    [[ADD:%.*]] = add i16 [[CALL1]], [[CALL2]]
; CHECK-NEXT:    [[ADD:%.*]] = add i16 7, [[CALL2]]
; CHECK-NEXT:    ret i16 [[ADD]]
;
  %call1 = call i16 (i16, ...) @vararg_prop(i16 7, i16 8, i16 %a)
@@ -68,10 +67,6 @@ define dso_local i16 @vararg_tests(i16 %a) {
}

define internal i16 @vararg_prop(i16 %p1, ...) {
; CHECK-LABEL: define {{[^@]+}}@vararg_prop
; CHECK-SAME: (i16 returned [[P1:%.*]], ...)
; CHECK-NEXT:    ret i16 7
;
  ret i16 %p1
}

+1 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ define dso_local void @foo(i32 %N) {
; CHECK-NEXT:    store i32 [[N]], i32* [[N_ADDR]], align 4
; CHECK-NEXT:    store float 3.000000e+00, float* [[P]], align 4
; CHECK-NEXT:    store i32 7, i32* [[N_ADDR]], align 4
; CHECK-NEXT:    call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull align 8 dereferenceable(24) @1, i32 3, void (i32*, i32*, ...)* nonnull bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* noalias nocapture nonnull readonly align 4 dereferenceable(4) [[N_ADDR]], float* noalias nocapture nonnull readonly align 4 dereferenceable(4) [[P]], i64 4617315517961601024)
; CHECK-NEXT:    call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull align 8 dereferenceable(24) @1, i32 3, void (i32*, i32*, ...)* nonnull bitcast (void (i32*, i32*, i32*, float*, i64)* @.omp_outlined. to void (i32*, i32*, ...)*), i32* noalias nocapture nonnull readonly align 4 dereferenceable(4) [[N_ADDR]], float* noalias nocapture nonnull readonly align 4 dereferenceable(4) [[P]], i64 undef)
; CHECK-NEXT:    ret void
;
entry:
Loading