From 71b80f9e8ce1da544ca6ec2df3358f634f10c0ac Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 17 Feb 2012 18:59:53 +0000 Subject: [PATCH] Calls and invokes with the new clang.arc.no_objc_arc_exceptions metadata may still unwind, but only in ways that the ARC optimizer doesn't need to consider. This permits more aggressive optimization. llvm-svn: 150829 --- include/llvm/Support/CFG.h | 6 + lib/Transforms/Scalar/ObjCARC.cpp | 53 +++++++- .../ObjCARC/no-objc-arc-exceptions.ll | 122 ++++++++++++++++++ 3 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 test/Transforms/ObjCARC/no-objc-arc-exceptions.ll diff --git a/include/llvm/Support/CFG.h b/include/llvm/Support/CFG.h index 6e354f9a1e0..f5dc8ea055a 100644 --- a/include/llvm/Support/CFG.h +++ b/include/llvm/Support/CFG.h @@ -71,6 +71,12 @@ public: unsigned getOperandNo() const { return It.getOperandNo(); } + + /// getUse - Return the operand Use in the predecessor's terminator + /// of the successor. + Use &getUse() const { + return It.getUse(); + } }; typedef PredIterator pred_iterator; diff --git a/lib/Transforms/Scalar/ObjCARC.cpp b/lib/Transforms/Scalar/ObjCARC.cpp index 673e1a4bbb4..dd3e7589bd6 100644 --- a/lib/Transforms/Scalar/ObjCARC.cpp +++ b/lib/Transforms/Scalar/ObjCARC.cpp @@ -1655,6 +1655,10 @@ namespace { /// metadata. unsigned CopyOnEscapeMDKind; + /// NoObjCARCExceptionsMDKind - The Metadata Kind for + /// clang.arc.no_objc_arc_exceptions metadata. + unsigned NoObjCARCExceptionsMDKind; + Constant *getRetainRVCallee(Module *M); Constant *getAutoreleaseRVCallee(Module *M); Constant *getReleaseCallee(Module *M); @@ -2406,7 +2410,15 @@ ObjCARCOpt::CheckForCFGHazards(const BasicBlock *BB, bool SomeSuccHasSame = false; bool AllSuccsHaveSame = true; PtrState &S = MyStates.getPtrTopDownState(Arg); - for (succ_const_iterator SI(TI), SE(TI, false); SI != SE; ++SI) { + succ_const_iterator SI(TI), SE(TI, false); + + // If the terminator is an invoke marked with the + // clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be + // ignored, for ARC purposes. + if (isa(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) + --SE; + + for (; SI != SE; ++SI) { PtrState &SuccS = BBStates[*SI].getPtrBottomUpState(Arg); switch (SuccS.GetSeq()) { case S_None: @@ -2441,7 +2453,15 @@ ObjCARCOpt::CheckForCFGHazards(const BasicBlock *BB, bool SomeSuccHasSame = false; bool AllSuccsHaveSame = true; PtrState &S = MyStates.getPtrTopDownState(Arg); - for (succ_const_iterator SI(TI), SE(TI, false); SI != SE; ++SI) { + succ_const_iterator SI(TI), SE(TI, false); + + // If the terminator is an invoke marked with the + // clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be + // ignored, for ARC purposes. + if (isa(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) + --SE; + + for (; SI != SE; ++SI) { PtrState &SuccS = BBStates[*SI].getPtrBottomUpState(Arg); switch (SuccS.GetSeq()) { case S_None: { @@ -2486,7 +2506,13 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB, succ_const_iterator SI(TI), SE(TI, false); if (SI == SE) MyStates.SetAsExit(); - else + else { + // If the terminator is an invoke marked with the + // clang.arc.no_objc_arc_exceptions metadata, the unwind edge can be + // ignored, for ARC purposes. + if (isa(TI) && TI->getMetadata(NoObjCARCExceptionsMDKind)) + --SE; + do { const BasicBlock *Succ = *SI++; if (Succ == BB) @@ -2507,6 +2533,7 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB, } break; } while (SI != SE); + } // Visit all the instructions, bottom-up. for (BasicBlock::iterator I = BB->end(), E = BB->begin(); I != E; --I) { @@ -2668,7 +2695,18 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB, MyStates.SetAsEntry(); else do { - const BasicBlock *Pred = *PI++; + unsigned OperandNo = PI.getOperandNo(); + const Use &Us = PI.getUse(); + ++PI; + + // Skip invoke unwind edges on invoke instructions marked with + // clang.arc.no_objc_arc_exceptions. + if (const InvokeInst *II = dyn_cast(Us.getUser())) + if (OperandNo == II->getNumArgOperands() + 2 && + II->getMetadata(NoObjCARCExceptionsMDKind)) + continue; + + const BasicBlock *Pred = cast(Us.getUser())->getParent(); if (Pred == BB) continue; DenseMap::iterator I = BBStates.find(Pred); @@ -2850,7 +2888,8 @@ ComputePostOrders(Function &F, OnStack.insert(EntryBB); do { dfs_next_succ: - succ_iterator End = succ_end(SuccStack.back().first); + TerminatorInst *TI = cast(&SuccStack.back().first->back()); + succ_iterator End = succ_iterator(TI, true); while (SuccStack.back().second != End) { BasicBlock *BB = *SuccStack.back().second++; if (Visited.insert(BB)) { @@ -2871,7 +2910,7 @@ ComputePostOrders(Function &F, SmallVector Exits; for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I) { BasicBlock *BB = I; - if (BB->getTerminator()->getNumSuccessors() == 0) + if (cast(&BB->back())->getNumSuccessors() == 0) Exits.push_back(BB); } @@ -3507,6 +3546,8 @@ bool ObjCARCOpt::doInitialization(Module &M) { M.getContext().getMDKindID("clang.imprecise_release"); CopyOnEscapeMDKind = M.getContext().getMDKindID("clang.arc.copy_on_escape"); + NoObjCARCExceptionsMDKind = + M.getContext().getMDKindID("clang.arc.no_objc_arc_exceptions"); // Intuitively, objc_retain and others are nocapture, however in practice // they are not, because they return their argument value. And objc_release diff --git a/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll b/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll new file mode 100644 index 00000000000..9728f6e0d94 --- /dev/null +++ b/test/Transforms/ObjCARC/no-objc-arc-exceptions.ll @@ -0,0 +1,122 @@ +; RUN: opt -S -objc-arc < %s | FileCheck %s + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +%struct.__block_byref_x = type { i8*, %struct.__block_byref_x*, i32, i32, i32 } +%struct.__block_descriptor = type { i64, i64 } +@_NSConcreteStackBlock = external global i8* +@__block_descriptor_tmp = external hidden constant { i64, i64, i8*, i8*, i8*, i8* } + +; The optimizer should make use of the !clang.arc.no_objc_arc_exceptions +; metadata and eliminate the retainBlock+release pair here. +; rdar://10803830. + +; CHECK: define void @test0( +; CHECK-NOT: @objc +; CHECK: } +define void @test0() { +entry: + %x = alloca %struct.__block_byref_x, align 8 + %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8 + %byref.isa = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 0 + store i8* null, i8** %byref.isa, align 8 + %byref.forwarding = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 1 + store %struct.__block_byref_x* %x, %struct.__block_byref_x** %byref.forwarding, align 8 + %byref.flags = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 2 + store i32 0, i32* %byref.flags, align 8 + %byref.size = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 3 + store i32 32, i32* %byref.size, align 4 + %block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 0 + store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %block.isa, align 8 + %block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 1 + store i32 1107296256, i32* %block.flags, align 8 + %block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 2 + store i32 0, i32* %block.reserved, align 4 + %block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 3 + store i8* bitcast (void (i8*)* @__foo_block_invoke_0 to i8*), i8** %block.invoke, align 8 + %block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 4 + store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @__block_descriptor_tmp to %struct.__block_descriptor*), %struct.__block_descriptor** %block.descriptor, align 8 + %block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 5 + %t1 = bitcast %struct.__block_byref_x* %x to i8* + store i8* %t1, i8** %block.captured, align 8 + %t2 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block to i8* + %t3 = call i8* @objc_retainBlock(i8* %t2) nounwind, !clang.arc.copy_on_escape !4 + %t4 = getelementptr inbounds i8* %t3, i64 16 + %t5 = bitcast i8* %t4 to i8** + %t6 = load i8** %t5, align 8 + %t7 = bitcast i8* %t6 to void (i8*)* + invoke void %t7(i8* %t3) + to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !4 + +invoke.cont: ; preds = %entry + call void @objc_release(i8* %t3) nounwind, !clang.imprecise_release !4 + call void @_Block_object_dispose(i8* %t1, i32 8) + ret void + +lpad: ; preds = %entry + %t8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + call void @_Block_object_dispose(i8* %t1, i32 8) + resume { i8*, i32 } %t8 +} + +; There is no !clang.arc.no_objc_arc_exceptions +; metadata here, so the optimizer shouldn't eliminate anything. + +; CHECK: define void @test0_no_metadata( +; CHECK: call i8* @objc_retainBlock( +; CHECK: invoke +; CHECK: call void @objc_release( +; CHECK: } +define void @test0_no_metadata() { +entry: + %x = alloca %struct.__block_byref_x, align 8 + %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8 + %byref.isa = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 0 + store i8* null, i8** %byref.isa, align 8 + %byref.forwarding = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 1 + store %struct.__block_byref_x* %x, %struct.__block_byref_x** %byref.forwarding, align 8 + %byref.flags = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 2 + store i32 0, i32* %byref.flags, align 8 + %byref.size = getelementptr inbounds %struct.__block_byref_x* %x, i64 0, i32 3 + store i32 32, i32* %byref.size, align 4 + %block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 0 + store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %block.isa, align 8 + %block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 1 + store i32 1107296256, i32* %block.flags, align 8 + %block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 2 + store i32 0, i32* %block.reserved, align 4 + %block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 3 + store i8* bitcast (void (i8*)* @__foo_block_invoke_0 to i8*), i8** %block.invoke, align 8 + %block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 4 + store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @__block_descriptor_tmp to %struct.__block_descriptor*), %struct.__block_descriptor** %block.descriptor, align 8 + %block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block, i64 0, i32 5 + %t1 = bitcast %struct.__block_byref_x* %x to i8* + store i8* %t1, i8** %block.captured, align 8 + %t2 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>* %block to i8* + %t3 = call i8* @objc_retainBlock(i8* %t2) nounwind, !clang.arc.copy_on_escape !4 + %t4 = getelementptr inbounds i8* %t3, i64 16 + %t5 = bitcast i8* %t4 to i8** + %t6 = load i8** %t5, align 8 + %t7 = bitcast i8* %t6 to void (i8*)* + invoke void %t7(i8* %t3) + to label %invoke.cont unwind label %lpad + +invoke.cont: ; preds = %entry + call void @objc_release(i8* %t3) nounwind, !clang.imprecise_release !4 + call void @_Block_object_dispose(i8* %t1, i32 8) + ret void + +lpad: ; preds = %entry + %t8 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__objc_personality_v0 to i8*) + cleanup + call void @_Block_object_dispose(i8* %t1, i32 8) + resume { i8*, i32 } %t8 +} + +declare i8* @objc_retainBlock(i8*) +declare void @objc_release(i8*) +declare void @_Block_object_dispose(i8*, i32) +declare i32 @__objc_personality_v0(...) +declare void @__foo_block_invoke_0(i8* nocapture) uwtable ssp + +!4 = metadata !{}