diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index b52a8977a7cc..6690a69b8d65 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1593,6 +1593,16 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, args.add(EmitAnyExprToTemp(E), type); } +// In ObjC ARC mode with no ObjC ARC exception safety, tell the ARC +// optimizer it can aggressively ignore unwind edges. +void +CodeGenFunction::AddObjCARCExceptionMetadata(llvm::Instruction *Inst) { + if (CGM.getCodeGenOpts().OptimizationLevel != 0 && + !CGM.getCodeGenOpts().ObjCAutoRefCountExceptions) + Inst->setMetadata("clang.arc.no_objc_arc_exceptions", + CGM.getNoObjCARCExceptionsMetadata()); +} + /// Emits a call or invoke instruction to the given function, depending /// on the current state of the EH stack. llvm::CallSite @@ -1600,14 +1610,22 @@ CodeGenFunction::EmitCallOrInvoke(llvm::Value *Callee, ArrayRef Args, const Twine &Name) { llvm::BasicBlock *InvokeDest = getInvokeDest(); - if (!InvokeDest) - return Builder.CreateCall(Callee, Args, Name); - llvm::BasicBlock *ContBB = createBasicBlock("invoke.cont"); - llvm::InvokeInst *Invoke = Builder.CreateInvoke(Callee, ContBB, InvokeDest, - Args, Name); - EmitBlock(ContBB); - return Invoke; + llvm::Instruction *Inst; + if (!InvokeDest) + Inst = Builder.CreateCall(Callee, Args, Name); + else { + llvm::BasicBlock *ContBB = createBasicBlock("invoke.cont"); + Inst = Builder.CreateInvoke(Callee, ContBB, InvokeDest, Args, Name); + EmitBlock(ContBB); + } + + // In ObjC ARC mode with no ObjC ARC exception safety, tell the ARC + // optimizer it can aggressively ignore unwind edges. + if (CGM.getLangOptions().ObjCAutoRefCount) + AddObjCARCExceptionMetadata(Inst); + + return Inst; } llvm::CallSite @@ -1916,6 +1934,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, CS.setAttributes(Attrs); CS.setCallingConv(static_cast(CallingConv)); + // In ObjC ARC mode with no ObjC ARC exception safety, tell the ARC + // optimizer it can aggressively ignore unwind edges. + if (CGM.getLangOptions().ObjCAutoRefCount) + AddObjCARCExceptionMetadata(CS.getInstruction()); + // If the call doesn't return, finish the basic block and clear the // insertion point; this allows the rest of IRgen to discard // unreachable code. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9081ece6f8f1..263a0639fa04 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2566,6 +2566,8 @@ private: CodeGenModule::ByrefHelpers * buildByrefHelpers(llvm::StructType &byrefType, const AutoVarEmission &emission); + + void AddObjCARCExceptionMetadata(llvm::Instruction *Inst); }; /// Helper class with most of the code for saving a value for a diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 2ebea592641e..dd613b4014f9 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -69,7 +69,8 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, Types(C, M, TD, getTargetCodeGenInfo().getABIInfo(), ABI, CGO), TBAA(0), VTables(*this), ObjCRuntime(0), OpenCLRuntime(0), CUDARuntime(0), - DebugInfo(0), ARCData(0), RRData(0), CFConstantStringClassRef(0), + DebugInfo(0), ARCData(0), NoObjCARCExceptionsMetadata(0), + RRData(0), CFConstantStringClassRef(0), ConstantStringClassRef(0), NSConstantStringType(0), VMContext(M.getContext()), NSConcreteGlobalBlock(0), NSConcreteStackBlock(0), diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 6813edd5f185..9b6931d5aa7d 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -233,6 +233,7 @@ class CodeGenModule : public CodeGenTypeCache { CGCUDARuntime* CUDARuntime; CGDebugInfo* DebugInfo; ARCEntrypoints *ARCData; + llvm::MDNode *NoObjCARCExceptionsMetadata; RREntrypoints *RRData; // WeakRefReferences - A set of references that have only been seen via @@ -421,6 +422,14 @@ public: CGDebugInfo *getModuleDebugInfo() { return DebugInfo; } + llvm::MDNode *getNoObjCARCExceptionsMetadata() { + if (!NoObjCARCExceptionsMetadata) + NoObjCARCExceptionsMetadata = + llvm::MDNode::get(getLLVMContext(), + SmallVector()); + return NoObjCARCExceptionsMetadata; + } + ASTContext &getContext() const { return Context; } const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } const LangOptions &getLangOptions() const { return Features; } diff --git a/clang/test/CodeGenObjC/arc-no-arc-exceptions.m b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m new file mode 100644 index 000000000000..d03a0c9322f0 --- /dev/null +++ b/clang/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fblocks -fexceptions -fobjc-exceptions -O2 -disable-llvm-optzns -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fblocks -fexceptions -fobjc-exceptions -O0 -disable-llvm-optzns -o - %s | FileCheck -check-prefix=NO-METADATA %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fblocks -fexceptions -fobjc-exceptions -O2 -disable-llvm-optzns -o - %s -fobjc-arc-exceptions | FileCheck -check-prefix=NO-METADATA %s + +// The front-end should emit clang.arc.no_objc_arc_exceptions in -fobjc-arc-exceptions +// mode when optimization is enabled, and not otherwise. + +void thrower(void); +void not(void) __attribute__((nothrow)); + +// CHECK: define void @test0( +// CHECK: call void @thrower(), !clang.arc.no_objc_arc_exceptions !0 +// CHECK: call void @not() nounwind, !clang.arc.no_objc_arc_exceptions !0 +// NO-METADATA: define void @test0( +// NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions +// NO-METADATA: } +void test0(void) { + thrower(); + not(); +} + +// CHECK: define void @test1( +// CHECK: call void @thrower(), !clang.arc.no_objc_arc_exceptions !0 +// CHECK: call void @not() nounwind, !clang.arc.no_objc_arc_exceptions !0 +// NO-METADATA: define void @test1( +// NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions +// NO-METADATA: } +void test1(id x) { + id y = x; + thrower(); + not(); +} + +void NSLog(id, ...); + +// CHECK: define void @test2( +// CHECK: invoke void (i8*, ...)* @NSLog(i8* bitcast (%struct.NSConstantString* @_unnamed_cfstring_ to i8*), i32* %x2) +// CHECK: to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 +// NO-METADATA: define void @test2( +// NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions +// NO-METADATA: } +void test2(void) { + @autoreleasepool { + __attribute__((__blocks__(byref))) int x; + NSLog(@"Address of x outside of block: %p", &x); + } +} + +// CHECK: define void @test3( +// CHECK: invoke void %9(i8* %7) +// CHECK: to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 +// NO-METADATA: define void @test3( +// NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions +// NO-METADATA: } +void test3(void) { + @autoreleasepool { + __attribute__((__blocks__(byref))) int x; + ^{ + NSLog(@"Address of x in non-assigned block: %p", &x); + }(); + } +} + +// CHECK: define void @test4( +// CHECK: invoke void %13(i8* %11) +// CHECK: to label %invoke.cont unwind label %lpad, !clang.arc.no_objc_arc_exceptions !0 +// NO-METADATA: define void @test4( +// NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions +// NO-METADATA: } +void test4(void) { + @autoreleasepool { + __attribute__((__blocks__(byref))) int x; + void (^b)(void) = ^{ + NSLog(@"Address of x in assigned block: %p", &x); + }; + b(); + } +}