Commit 6770fbb3 authored by Akira Hatanaka's avatar Akira Hatanaka
Browse files

[ObjC][ARC] Delete ARC runtime calls that take inert phi values

This improves on the following patch, which removed ARC runtime calls
taking inert global variables:

https://reviews.llvm.org/D62433

rdar://problem/59137105
parent ba9cae58
Loading
Loading
Loading
Loading
+32 −12
Original line number Diff line number Diff line
@@ -877,23 +877,43 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
  optimizeDelayedAutoreleaseRV();
}

/// This function returns true if the value is inert. An ObjC ARC runtime call
/// taking an inert operand can be safely deleted.
static bool isInertARCValue(Value *V) {
  V = V->stripPointerCasts();

  if (IsNullOrUndef(V))
    return true;

  // See if this is a global attribute annotated with an 'objc_arc_inert'.
  if (auto *GV = dyn_cast<GlobalVariable>(V))
    if (GV->hasAttribute("objc_arc_inert"))
      return true;

  if (auto PN = dyn_cast<PHINode>(V)) {
    // Look through phis's operands.
    for (Value *Opnd : PN->incoming_values())
      if (!isInertARCValue(Opnd))
        return false;
    return true;
  }

  return false;
}

void ObjCARCOpt::OptimizeIndividualCallImpl(
    Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
    Instruction *Inst, ARCInstKind Class, const Value *Arg) {
  LLVM_DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n");

  // Some of the ARC calls can be deleted if their arguments are global
  // variables that are inert in ARC.
  if (IsNoopOnGlobal(Class)) {
    Value *Opnd = Inst->getOperand(0);
    if (auto *GV = dyn_cast<GlobalVariable>(Opnd->stripPointerCasts()))
      if (GV->hasAttribute("objc_arc_inert")) {
  // We can delete this call if it takes an inert value.
  if (IsNoopOnGlobal(Class))
    if (isInertARCValue(Inst->getOperand(0))) {
      if (!Inst->getType()->isVoidTy())
          Inst->replaceAllUsesWith(Opnd);
        Inst->replaceAllUsesWith(Inst->getOperand(0));
      Inst->eraseFromParent();
      return;
    }
  }

  switch (Class) {
  default:
+33 −0
Original line number Diff line number Diff line
@@ -54,6 +54,39 @@ define internal void @__globalBlock_block_invoke(i8* nocapture readnone) {
  ret void
}

; CHECK: define %[[V0:.*]]* @test_conditional0(
; CHECK: %[[PHI0:.*]] = phi %[[V0]]* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %[[V0]]*), %{{.*}} ], [ null, %{{.*}} ]

; CHECK: %[[PHI1:.*]] = phi %[[V0]]* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %[[V0]]*), %{{.*}} ], [ %[[PHI0]], %{{.*}} ]
; CHECK-NEXT: %[[PHI2:.*]] = phi %[[V0]]* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %[[V0]]*), %{{.*}} ], [ %{{.*}}, %{{.*}} ]
; CHECK-NEXT: %[[V2:.*]] = bitcast %[[V0]]* %[[PHI1]] to i8*
; CHECK-NEXT: %[[V4:.*]] = bitcast %[[V0]]* %[[PHI2]] to i8*
; CHECK-NEXT: %[[V5:.*]] = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %[[V4]])
; CHECK-NEXT: ret %[[V0]]* %[[PHI2]]

define %0* @test_conditional0(i32 %i, %0* %b) {
entry:
  %v0 = icmp eq i32 %i, 1
  br i1 %v0, label %bb2, label %bb1

bb1:
  %v1 = icmp eq i32 %i, 2
  br i1 %v1, label %bb2, label %return

bb2:
  %phi0 = phi %0* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %0*), %entry ], [ null, %bb1 ]
  br label %return

return:
  %phi1 = phi %0* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %0*), %bb1 ], [ %phi0, %bb2 ]
  %phi2 = phi %0* [ bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to %0*), %bb1 ], [ %b, %bb2 ]
  %v2 = bitcast %0* %phi1 to i8*
  %v3 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %v2)
  %v4 = bitcast %0* %phi2 to i8*
  %v5 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %v4)
  ret %0* %phi2
}

declare void @foo()

declare i8* @llvm.objc.retain(i8*) local_unnamed_addr