From d9eece916a8a9b370e1f90e6461c612d12c55729 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Stefan=20Gr=C3=A4nitz?= Date: Tue, 24 Jan 2023 11:37:23 +0100 Subject: [PATCH] [ObjC][ARC] Teach the OptimizeSequences step of ObjCARCOpts about WinEH funclet tokens When optimizing retain-release-sequences we insert (and delete) ObjC runtime calls. These calls need a funclet operand bundle that refers to the enclosing funclet pad whenever they are inserted in a WinEH funclet. WinEH funclets can contain multiple basic blocks. In order to find the enclosing funclet pad, we have to calculate the funclet coloring first. Reviewed By: ahatanak Differential Revision: https://reviews.llvm.org/D137944 --- clang/test/CodeGenObjCXX/arc-exceptions-seh.mm | 22 ++-- llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp | 124 +++++++++++++---------- llvm/test/Transforms/ObjCARC/funclet-catchpad.ll | 40 ++++++++ 3 files changed, 120 insertions(+), 66 deletions(-) create mode 100644 llvm/test/Transforms/ObjCARC/funclet-catchpad.ll diff --git a/clang/test/CodeGenObjCXX/arc-exceptions-seh.mm b/clang/test/CodeGenObjCXX/arc-exceptions-seh.mm index d5da111..0b697be 100644 --- a/clang/test/CodeGenObjCXX/arc-exceptions-seh.mm +++ b/clang/test/CodeGenObjCXX/arc-exceptions-seh.mm @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fobjc-arc-exceptions -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-O0 -// RUN: %clang_cc1 -O2 -triple x86_64-pc-windows-msvc -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fobjc-arc-exceptions -fobjc-runtime=gnustep-2.0 -mllvm -enable-objc-arc-opts=false -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-O2 +// RUN: %clang_cc1 -O0 -triple x86_64-pc-windows-msvc -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fobjc-arc-exceptions -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64-pc-windows-msvc -emit-llvm -fobjc-arc -fexceptions -fobjc-exceptions -fobjc-arc-exceptions -fobjc-runtime=gnustep-2.0 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-O2 // WinEH requires funclet tokens on nounwind intrinsics if they can lower to // regular function calls in the course of IR transformations. @@ -24,7 +24,9 @@ void try_catch_with_objc_intrinsic() { // CHECK-LABEL: try_catch_with_objc_intrinsic // // CHECK: catch.dispatch: -// CHECK-NEXT: [[CATCHSWITCH:%[0-9]+]] = catchswitch within none [label %catch] unwind label %[[CLEANUP1:.*]] +// CHECK-NEXT: [[CATCHSWITCH:%[0-9]+]] = catchswitch within none [label %catch] +// CHECK-O0: unwind label %[[CLEANUP1:.*]] +// CHECK-O2: unwind to caller // // All calls within a catchpad must have funclet tokens that refer to it: // CHECK: catch: @@ -58,12 +60,12 @@ void try_catch_with_objc_intrinsic() { // CHECK-O2: @llvm.objc.release // CHECK: [ "funclet"(token [[CLEANUPPAD2]]) ] // CHECK: cleanupret from [[CLEANUPPAD2]] -// CHECK: unwind label %[[CLEANUP1]] +// CHECK-O0: unwind label %[[CLEANUP1]] +// CHECK-O2: unwind to caller // -// CHECK: [[CLEANUP1]]: -// CHECK-NEXT: [[CLEANUPPAD1:%[0-9]+]] = cleanuppad within none -// CHECK: call +// CHECK-O0: [[CLEANUP1]]: +// CHECK-O0-NEXT: [[CLEANUPPAD1:%[0-9]+]] = cleanuppad within none +// CHECK-O0: call // CHECK-O0: @llvm.objc.storeStrong -// CHECK-O2: @llvm.objc.release -// CHECK: [ "funclet"(token [[CLEANUPPAD1]]) ] -// CHECK: cleanupret from [[CLEANUPPAD1]] unwind to caller +// CHECK-O0: [ "funclet"(token [[CLEANUPPAD1]]) ] +// CHECK-O0: cleanupret from [[CLEANUPPAD1]] unwind to caller diff --git a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp index 927f389..5108598 100644 --- a/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp +++ b/llvm/lib/Transforms/ObjCARC/ObjCARCOpts.cpp @@ -501,6 +501,8 @@ class ObjCARCOpt { /// is in fact used in the current function. unsigned UsedInThisFunction; + DenseMap BlockEHColors; + bool OptimizeRetainRVCall(Function &F, Instruction *RetainRV); void OptimizeAutoreleaseRVCall(Function &F, Instruction *AutoreleaseRV, ARCInstKind &Class); @@ -508,17 +510,16 @@ class ObjCARCOpt { /// Optimize an individual call, optionally passing the /// GetArgRCIdentityRoot if it has already been computed. - void OptimizeIndividualCallImpl( - Function &F, DenseMap &BlockColors, - Instruction *Inst, ARCInstKind Class, const Value *Arg); + void OptimizeIndividualCallImpl(Function &F, Instruction *Inst, + ARCInstKind Class, const Value *Arg); /// Try to optimize an AutoreleaseRV with a RetainRV or UnsafeClaimRV. If the /// optimization occurs, returns true to indicate that the caller should /// assume the instructions are dead. - bool OptimizeInlinedAutoreleaseRVCall( - Function &F, DenseMap &BlockColors, - Instruction *Inst, const Value *&Arg, ARCInstKind Class, - Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg); + bool OptimizeInlinedAutoreleaseRVCall(Function &F, Instruction *Inst, + const Value *&Arg, ARCInstKind Class, + Instruction *AutoreleaseRV, + const Value *&AutoreleaseRVArg); void CheckForCFGHazards(const BasicBlock *BB, DenseMap &BBStates, @@ -566,12 +567,46 @@ class ObjCARCOpt { void OptimizeReturns(Function &F); + Instruction *cloneCallInstForBB(CallInst &CI, BasicBlock &BB) { + SmallVector OpBundles; + for (unsigned I = 0, E = CI.getNumOperandBundles(); I != E; ++I) { + auto Bundle = CI.getOperandBundleAt(I); + // Funclets will be reassociated in the future. + if (Bundle.getTagID() == LLVMContext::OB_funclet) + continue; + OpBundles.emplace_back(Bundle); + } + + if (!BlockEHColors.empty()) { + const ColorVector &CV = BlockEHColors.find(&BB)->second; + assert(CV.size() > 0 && "non-unique color for block!"); + Instruction *EHPad = CV.front()->getFirstNonPHI(); + if (EHPad->isEHPad()) + OpBundles.emplace_back("funclet", EHPad); + } + + return CallInst::Create(&CI, OpBundles); + } + + void addOpBundleForFunclet(BasicBlock *BB, + SmallVectorImpl &OpBundles) { + if (!BlockEHColors.empty()) { + const ColorVector &CV = BlockEHColors.find(BB)->second; + assert(CV.size() > 0 && "Uncolored block"); + for (BasicBlock *EHPadBB : CV) + if (auto *EHPad = dyn_cast(EHPadBB->getFirstNonPHI())) { + OpBundles.emplace_back("funclet", EHPad); + return; + } + } + } + #ifndef NDEBUG void GatherStatistics(Function &F, bool AfterOptimization = false); #endif public: - void init(Module &M); + void init(Function &F); bool run(Function &F, AAResults &AA); bool hasCFGChanged() const { return CFGChanged; } }; @@ -624,8 +659,7 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) { } bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall( - Function &F, DenseMap &BlockColors, - Instruction *Inst, const Value *&Arg, ARCInstKind Class, + Function &F, Instruction *Inst, const Value *&Arg, ARCInstKind Class, Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg) { if (BundledInsts->contains(Inst)) return false; @@ -678,8 +712,7 @@ bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall( EraseInstruction(Inst); // Run the normal optimizations on Release. - OptimizeIndividualCallImpl(F, BlockColors, Release, ARCInstKind::Release, - Arg); + OptimizeIndividualCallImpl(F, Release, ARCInstKind::Release, Arg); return true; } @@ -732,31 +765,6 @@ void ObjCARCOpt::OptimizeAutoreleaseRVCall(Function &F, LLVM_DEBUG(dbgs() << "New: " << *AutoreleaseRV << "\n"); } -namespace { -Instruction * -CloneCallInstForBB(CallInst &CI, BasicBlock &BB, - const DenseMap &BlockColors) { - SmallVector OpBundles; - for (unsigned I = 0, E = CI.getNumOperandBundles(); I != E; ++I) { - auto Bundle = CI.getOperandBundleAt(I); - // Funclets will be reassociated in the future. - if (Bundle.getTagID() == LLVMContext::OB_funclet) - continue; - OpBundles.emplace_back(Bundle); - } - - if (!BlockColors.empty()) { - const ColorVector &CV = BlockColors.find(&BB)->second; - assert(CV.size() == 1 && "non-unique color for block!"); - Instruction *EHPad = CV.front()->getFirstNonPHI(); - if (EHPad->isEHPad()) - OpBundles.emplace_back("funclet", EHPad); - } - - return CallInst::Create(&CI, OpBundles); -} -} - /// Visit each call, one at a time, and make simplifications without doing any /// additional analysis. void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { @@ -764,11 +772,6 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { // Reset all the flags in preparation for recomputing them. UsedInThisFunction = 0; - DenseMap BlockColors; - if (F.hasPersonalityFn() && - isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) - BlockColors = colorEHFunclets(F); - // Store any delayed AutoreleaseRV intrinsics, so they can be easily paired // with RetainRV and UnsafeClaimRV. Instruction *DelayedAutoreleaseRV = nullptr; @@ -781,7 +784,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { auto optimizeDelayedAutoreleaseRV = [&]() { if (!DelayedAutoreleaseRV) return; - OptimizeIndividualCallImpl(F, BlockColors, DelayedAutoreleaseRV, + OptimizeIndividualCallImpl(F, DelayedAutoreleaseRV, ARCInstKind::AutoreleaseRV, DelayedAutoreleaseRVArg); setDelayedAutoreleaseRV(nullptr); @@ -844,7 +847,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { case ARCInstKind::UnsafeClaimRV: if (DelayedAutoreleaseRV) { // We have a potential RV pair. Check if they cancel out. - if (OptimizeInlinedAutoreleaseRVCall(F, BlockColors, Inst, Arg, Class, + if (OptimizeInlinedAutoreleaseRVCall(F, Inst, Arg, Class, DelayedAutoreleaseRV, DelayedAutoreleaseRVArg)) { setDelayedAutoreleaseRV(nullptr); @@ -855,7 +858,7 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) { break; } - OptimizeIndividualCallImpl(F, BlockColors, Inst, Class, Arg); + OptimizeIndividualCallImpl(F, Inst, Class, Arg); } // Catch the final delayed AutoreleaseRV. @@ -889,9 +892,9 @@ static bool isInertARCValue(Value *V, SmallPtrSet &VisitedPhis) { return false; } -void ObjCARCOpt::OptimizeIndividualCallImpl( - Function &F, DenseMap &BlockColors, - Instruction *Inst, ARCInstKind Class, const Value *Arg) { +void ObjCARCOpt::OptimizeIndividualCallImpl(Function &F, Instruction *Inst, + ARCInstKind Class, + const Value *Arg) { LLVM_DEBUG(dbgs() << "Visiting: Class: " << Class << "; " << *Inst << "\n"); // We can delete this call if it takes an inert value. @@ -1149,8 +1152,8 @@ void ObjCARCOpt::OptimizeIndividualCallImpl( continue; Value *Op = PN->getIncomingValue(i); Instruction *InsertPos = &PN->getIncomingBlock(i)->back(); - CallInst *Clone = cast( - CloneCallInstForBB(*CInst, *InsertPos->getParent(), BlockColors)); + CallInst *Clone = + cast(cloneCallInstForBB(*CInst, *InsertPos->getParent())); if (Op->getType() != ParamTy) Op = new BitCastInst(Op, ParamTy, "", InsertPos); Clone->setArgOperand(0, Op); @@ -1772,7 +1775,9 @@ void ObjCARCOpt::MoveCalls(Value *Arg, RRInfo &RetainsToMove, Value *MyArg = ArgTy == ParamTy ? Arg : new BitCastInst(Arg, ParamTy, "", InsertPt); Function *Decl = EP.get(ARCRuntimeEntryPointKind::Retain); - CallInst *Call = CallInst::Create(Decl, MyArg, "", InsertPt); + SmallVector BundleList; + addOpBundleForFunclet(InsertPt->getParent(), BundleList); + CallInst *Call = CallInst::Create(Decl, MyArg, BundleList, "", InsertPt); Call->setDoesNotThrow(); Call->setTailCall(); @@ -1785,7 +1790,9 @@ void ObjCARCOpt::MoveCalls(Value *Arg, RRInfo &RetainsToMove, Value *MyArg = ArgTy == ParamTy ? Arg : new BitCastInst(Arg, ParamTy, "", InsertPt); Function *Decl = EP.get(ARCRuntimeEntryPointKind::Release); - CallInst *Call = CallInst::Create(Decl, MyArg, "", InsertPt); + SmallVector BundleList; + addOpBundleForFunclet(InsertPt->getParent(), BundleList); + CallInst *Call = CallInst::Create(Decl, MyArg, BundleList, "", InsertPt); // Attach a clang.imprecise_release metadata tag, if appropriate. if (MDNode *M = ReleasesToMove.ReleaseMetadata) Call->setMetadata(MDKindCache.get(ARCMDKindID::ImpreciseRelease), M); @@ -2401,17 +2408,22 @@ ObjCARCOpt::GatherStatistics(Function &F, bool AfterOptimization) { } #endif -void ObjCARCOpt::init(Module &M) { +void ObjCARCOpt::init(Function &F) { if (!EnableARCOpts) return; // Intuitively, objc_retain and others are nocapture, however in practice // they are not, because they return their argument value. And objc_release // calls finalizers which can have arbitrary side effects. - MDKindCache.init(&M); + MDKindCache.init(F.getParent()); // Initialize our runtime entry point cache. - EP.init(&M); + EP.init(F.getParent()); + + // Compute which blocks are in which funclet. + if (F.hasPersonalityFn() && + isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) + BlockEHColors = colorEHFunclets(F); } bool ObjCARCOpt::run(Function &F, AAResults &AA) { @@ -2487,7 +2499,7 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) { PreservedAnalyses ObjCARCOptPass::run(Function &F, FunctionAnalysisManager &AM) { ObjCARCOpt OCAO; - OCAO.init(*F.getParent()); + OCAO.init(F); bool Changed = OCAO.run(F, AM.getResult(F)); bool CFGChanged = OCAO.hasCFGChanged(); diff --git a/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll b/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll new file mode 100644 index 0000000..9f047c2 --- /dev/null +++ b/llvm/test/Transforms/ObjCARC/funclet-catchpad.ll @@ -0,0 +1,40 @@ +; RUN: opt -mtriple=x86_64-windows-msvc -passes=objc-arc -S < %s | FileCheck %s + +; Check that funclet tokens are preserved +; +; CHECK-LABEL: catch: +; CHECK: %1 = catchpad within %0 +; CHECK: %2 = tail call ptr @llvm.objc.retain(ptr %exn) #0 [ "funclet"(token %1) ] +; CHECK: call void @llvm.objc.release(ptr %exn) #0 [ "funclet"(token %1) ] +; CHECK: catchret from %1 to label %eh.cont + +define void @try_catch_with_objc_intrinsic() personality ptr @__CxxFrameHandler3 { +entry: + %exn.slot = alloca ptr, align 8 + invoke void @may_throw(ptr null) to label %eh.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %entry + %0 = catchswitch within none [label %catch] unwind to caller + +eh.cont: ; preds = %catch, %entry + ret void + +catch: ; preds = %catch.dispatch + %1 = catchpad within %0 [ptr null, i32 0, ptr %exn.slot] + br label %if.then + +if.then: ; preds = %catch + %exn = load ptr, ptr null, align 8 + %2 = call ptr @llvm.objc.retain(ptr %exn) [ "funclet"(token %1) ] + call void @may_throw(ptr %exn) + call void @llvm.objc.release(ptr %exn) [ "funclet"(token %1) ] + catchret from %1 to label %eh.cont +} + +declare void @may_throw(ptr) +declare i32 @__CxxFrameHandler3(...) + +declare ptr @llvm.objc.retain(ptr) #0 +declare void @llvm.objc.release(ptr) #0 + +attributes #0 = { nounwind } -- 2.7.4