From 6770fbb31450db15d8d4a2ac7f0ce70eaf2acc5f Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 7 Feb 2020 16:24:18 -0800 Subject: [PATCH] [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 --- llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 44 ++++++++++++++------ llvm/test/Transforms/ObjCARC/inert-global.ll | 33 +++++++++++++++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index b80c1675050b..7dbb95faaf85 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -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(V)) + if (GV->hasAttribute("objc_arc_inert")) + return true; + + if (auto PN = dyn_cast(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 &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(Opnd->stripPointerCasts())) - if (GV->hasAttribute("objc_arc_inert")) { - if (!Inst->getType()->isVoidTy()) - Inst->replaceAllUsesWith(Opnd); - Inst->eraseFromParent(); - return; - } - } + // 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(Inst->getOperand(0)); + Inst->eraseFromParent(); + return; + } switch (Class) { default: diff --git a/llvm/test/Transforms/ObjCARC/inert-global.ll b/llvm/test/Transforms/ObjCARC/inert-global.ll index 4bb9a809a0f2..c23d2f98b949 100644 --- a/llvm/test/Transforms/ObjCARC/inert-global.ll +++ b/llvm/test/Transforms/ObjCARC/inert-global.ll @@ -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