diff --git a/llvm/include/llvm/CodeGen/WinEHFuncInfo.h b/llvm/include/llvm/CodeGen/WinEHFuncInfo.h --- a/llvm/include/llvm/CodeGen/WinEHFuncInfo.h +++ b/llvm/include/llvm/CodeGen/WinEHFuncInfo.h @@ -24,6 +24,7 @@ class AllocaInst; class BasicBlock; +class CallBase; class FuncletPadInst; class Function; class GlobalVariable; @@ -102,7 +103,7 @@ int getLastStateNumber() const { return CxxUnwindMap.size() - 1; } - void addIPToStateRange(const InvokeInst *II, MCSymbol *InvokeBegin, + void addIPToStateRange(const CallBase *CB, MCSymbol *InvokeBegin, MCSymbol *InvokeEnd); void addIPToStateRange(int State, MCSymbol *InvokeBegin, MCSymbol *InvokeEnd); diff --git a/llvm/lib/CodeGen/Analysis.cpp b/llvm/lib/CodeGen/Analysis.cpp --- a/llvm/lib/CodeGen/Analysis.cpp +++ b/llvm/lib/CodeGen/Analysis.cpp @@ -576,6 +576,11 @@ const Instruction *Term = ExitBB->getTerminator(); const ReturnInst *Ret = dyn_cast(Term); + // A call marked unwindabort cannot be tail called, because the unwinder must + // examine the call frame. + if (Call.isUnwindAbort()) + return false; + // The block must end in a return statement or unreachable. // // FIXME: Decline tailcall if it's not guaranteed and if the block ends in diff --git a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp --- a/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp @@ -211,9 +211,10 @@ /// Compute the call-site table. The entry for an invoke has a try-range /// containing the call, a non-zero landing pad, and an appropriate action. The /// entry for an ordinary call has a try-range containing the call and zero for -/// the landing pad and the action. Calls marked 'nounwind' have no entry and -/// must not be contained in the try-range of any entry - they form gaps in the -/// table. Entries must be ordered by try-range address. +/// the landing pad and the action. Calls marked 'unwindabort' have no entry +/// and must not be contained in the try-range of any entry - they form gaps in +/// the table. Calls marked 'nounwind' may be covered by anything -- either a +/// neighboring entry or a gap. Entries must be ordered by try-range address. /// /// Call-sites are split into one or more call-site ranges associated with /// different sections of the function. diff --git a/llvm/lib/CodeGen/DwarfEHPrepare.cpp b/llvm/lib/CodeGen/DwarfEHPrepare.cpp --- a/llvm/lib/CodeGen/DwarfEHPrepare.cpp +++ b/llvm/lib/CodeGen/DwarfEHPrepare.cpp @@ -69,9 +69,8 @@ /// Replace resumes that are not reachable from a cleanup landing pad with /// unreachable and then simplify those blocks. - size_t - pruneUnreachableResumes(SmallVectorImpl &Resumes, - SmallVectorImpl &CleanupLPads); + void pruneUnreachableResumes(SmallVectorImpl &Resumes, + SmallVectorImpl &CleanupLPads); /// Convert the ResumeInsts that are still present /// into calls to the appropriate _Unwind_Resume function. @@ -126,7 +125,7 @@ return ExnObj; } -size_t DwarfEHPrepare::pruneUnreachableResumes( +void DwarfEHPrepare::pruneUnreachableResumes( SmallVectorImpl &Resumes, SmallVectorImpl &CleanupLPads) { assert(DTU && "Should have DomTreeUpdater here."); @@ -145,7 +144,7 @@ // If everything is reachable, there is no change. if (ResumeReachable.all()) - return Resumes.size(); + return; LLVMContext &Ctx = F.getContext(); @@ -163,19 +162,22 @@ } } Resumes.resize(ResumesLeft); - return ResumesLeft; } bool DwarfEHPrepare::InsertUnwindResumeCalls() { - SmallVector Resumes; + SmallVector AbortResumes, NormalResumes; SmallVector CleanupLPads; if (F.doesNotThrow()) NumNoUnwind++; else NumUnwind++; for (BasicBlock &BB : F) { - if (auto *RI = dyn_cast(BB.getTerminator())) - Resumes.push_back(RI); + if (auto *RI = dyn_cast(BB.getTerminator())) { + if (RI->isUnwindAbort()) + AbortResumes.push_back(RI); + else + NormalResumes.push_back(RI); + } if (auto *LP = BB.getLandingPadInst()) if (LP->isCleanup()) CleanupLPads.push_back(LP); @@ -183,7 +185,7 @@ NumCleanupLandingPadsRemaining += CleanupLPads.size(); - if (Resumes.empty()) + if (NormalResumes.empty() && AbortResumes.empty()) return false; // Check the personality, don't do anything if it's scope-based. @@ -193,9 +195,9 @@ LLVMContext &Ctx = F.getContext(); - size_t ResumesLeft = Resumes.size(); if (OptLevel != CodeGenOpt::None) { - ResumesLeft = pruneUnreachableResumes(Resumes, CleanupLPads); + pruneUnreachableResumes(NormalResumes, CleanupLPads); + pruneUnreachableResumes(AbortResumes, CleanupLPads); #if LLVM_ENABLE_STATS unsigned NumRemainingLPs = 0; for (BasicBlock &BB : F) { @@ -208,7 +210,7 @@ #endif } - if (ResumesLeft == 0) + if (NormalResumes.empty() && AbortResumes.empty()) return true; // We pruned them all. // RewindFunction - _Unwind_Resume or the target equivalent. @@ -234,18 +236,14 @@ } RewindFunction = F.getParent()->getOrInsertFunction(RewindName, FTy); - // Create the basic block where the _Unwind_Resume call will live. - if (ResumesLeft == 1) { - // Instead of creating a new BB and PHI node, just append the call to - // _Unwind_Resume to the end of the single resume block. - ResumeInst *RI = Resumes.front(); - BasicBlock *UnwindBB = RI->getParent(); - Value *ExnObj = GetExceptionObject(RI); + // Helper to create the call instruction + auto CreateRewindCall = [&](BasicBlock *UnwindBB, Value *ExnObj, + bool UnwindAbort) { llvm::SmallVector RewindFunctionArgs; if (DoesRewindFunctionNeedExceptionObject) RewindFunctionArgs.push_back(ExnObj); - // Call the rewind function. + // Call the function. CallInst *CI = CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB); // The verifier requires that all calls of debug-info-bearing functions @@ -255,51 +253,56 @@ if (RewindFn && RewindFn->getSubprogram()) if (DISubprogram *SP = F.getSubprogram()) CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP)); + CI->setCallingConv(RewindFunctionCallingConv); + CI->setUnwindAbort(UnwindAbort); // We never expect _Unwind_Resume to return. CI->setDoesNotReturn(); new UnreachableInst(Ctx, UnwindBB); - return true; - } + }; + + // We may need to insert two separate blocks: one for the lowered 'resume' and + // one for the lowered 'resume unwindabort'. + for (auto &Resumes : {NormalResumes, AbortResumes}) { + if (Resumes.empty()) + continue; + // Create the basic block where the _Unwind_Resume call will live. + if (Resumes.size() == 1) { + // Instead of creating a new BB and PHI node, just append the call to + // _Unwind_Resume to the end of the single resume block. + ResumeInst *RI = Resumes.front(); + BasicBlock *UnwindBB = RI->getParent(); + Value *ExnObj = GetExceptionObject(RI); + CreateRewindCall(UnwindBB, ExnObj, RI->isUnwindAbort()); + continue; + } - std::vector Updates; - Updates.reserve(Resumes.size()); + std::vector Updates; + Updates.reserve(Resumes.size()); - llvm::SmallVector RewindFunctionArgs; + BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &F); + PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), Resumes.size(), + "exn.obj", UnwindBB); - BasicBlock *UnwindBB = BasicBlock::Create(Ctx, "unwind_resume", &F); - PHINode *PN = PHINode::Create(Type::getInt8PtrTy(Ctx), ResumesLeft, "exn.obj", - UnwindBB); + // Extract the exception object from the ResumeInst and add it to the PHI + // node that feeds the _Unwind_Resume call. + for (ResumeInst *RI : Resumes) { + BasicBlock *Parent = RI->getParent(); + BranchInst::Create(UnwindBB, Parent); + Updates.push_back({DominatorTree::Insert, Parent, UnwindBB}); - // Extract the exception object from the ResumeInst and add it to the PHI node - // that feeds the _Unwind_Resume call. - for (ResumeInst *RI : Resumes) { - BasicBlock *Parent = RI->getParent(); - BranchInst::Create(UnwindBB, Parent); - Updates.push_back({DominatorTree::Insert, Parent, UnwindBB}); + Value *ExnObj = GetExceptionObject(RI); + PN->addIncoming(ExnObj, Parent); - Value *ExnObj = GetExceptionObject(RI); - PN->addIncoming(ExnObj, Parent); + ++NumResumesLowered; + } - ++NumResumesLowered; + CreateRewindCall(UnwindBB, PN, Resumes.front()->isUnwindAbort()); + if (DTU) + DTU->applyUpdates(Updates); } - if (DoesRewindFunctionNeedExceptionObject) - RewindFunctionArgs.push_back(PN); - - // Call the function. - CallInst *CI = - CallInst::Create(RewindFunction, RewindFunctionArgs, "", UnwindBB); - CI->setCallingConv(RewindFunctionCallingConv); - - // We never expect _Unwind_Resume to return. - CI->setDoesNotReturn(); - new UnreachableInst(Ctx, UnwindBB); - - if (DTU) - DTU->applyUpdates(Updates); - return true; } diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2507,9 +2507,6 @@ if (isa(U)) return false; - if (CI.isInlineAsm()) - return translateInlineAsm(CI, MIRBuilder); - diagnoseDontCall(CI); Intrinsic::ID ID = Intrinsic::not_intrinsic; @@ -2519,8 +2516,27 @@ ID = static_cast(TII->getIntrinsicID(F)); } - if (!F || !F->isIntrinsic() || ID == Intrinsic::not_intrinsic) - return translateCallBase(CI, MIRBuilder); + if (!F || !F->isIntrinsic() || ID == Intrinsic::not_intrinsic) { + bool NeedEHLabel = CI.isUnwindAbort(); + MCSymbol *BeginSymbol = nullptr; + if (NeedEHLabel) { + MIRBuilder.buildInstr(TargetOpcode::G_INVOKE_REGION_START); + BeginSymbol = MF->getContext().createTempSymbol(); + MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(BeginSymbol); + } + if (CI.isInlineAsm()) { + if (!translateInlineAsm(CI, MIRBuilder)) + return false; + } else if (!translateCallBase(CI, MIRBuilder)) + return false; + + if (NeedEHLabel) { + MCSymbol *EndSymbol = MF->getContext().createTempSymbol(); + MIRBuilder.buildInstr(TargetOpcode::EH_LABEL).addSym(EndSymbol); + MF->addInvoke(nullptr, BeginSymbol, EndSymbol); + } + return true; + } assert(ID != Intrinsic::not_intrinsic && "unknown intrinsic"); diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1176,6 +1176,10 @@ return true; } + // Don't handle unwindabort calls yet + if (Call->isUnwindAbort()) + return false; + // Handle intrinsic function calls. if (const auto *II = dyn_cast(Call)) return selectIntrinsicCall(II); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -430,7 +430,8 @@ std::pair lowerInvokable(TargetLowering::CallLoweringInfo &CLI, - const BasicBlock *EHPadBB = nullptr); + const BasicBlock *EHPadBB = nullptr, + bool RequireEHLabels = false); /// When an MBB was split during scheduling, update the /// references that need to refer to the last resulting block. @@ -708,7 +709,7 @@ SDValue lowerStartEH(SDValue Chain, const BasicBlock *EHPadBB, MCSymbol *&BeginLabel); - SDValue lowerEndEH(SDValue Chain, const InvokeInst *II, + SDValue lowerEndEH(SDValue Chain, const CallBase *CB, const BasicBlock *EHPadBB, MCSymbol *BeginLabel); }; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7990,7 +7990,7 @@ return DAG.getEHLabel(getCurSDLoc(), Chain, BeginLabel); } -SDValue SelectionDAGBuilder::lowerEndEH(SDValue Chain, const InvokeInst *II, +SDValue SelectionDAGBuilder::lowerEndEH(SDValue Chain, const CallBase *CB, const BasicBlock *EHPadBB, MCSymbol *BeginLabel) { assert(BeginLabel && "BeginLabel should've been set"); @@ -8008,12 +8008,12 @@ // There is a platform (e.g. wasm) that uses funclet style IR but does not // actually use outlined funclets and their LSDA info style. if (MF.hasEHFunclets() && isFuncletEHPersonality(Pers)) { - assert(II && "II should've been set"); + assert(CB && "CB should've been set"); WinEHFuncInfo *EHInfo = MF.getWinEHFuncInfo(); - EHInfo->addIPToStateRange(II, BeginLabel, EndLabel); + EHInfo->addIPToStateRange(CB, BeginLabel, EndLabel); } else if (!isScopedEHPersonality(Pers)) { - assert(EHPadBB); - MF.addInvoke(FuncInfo.MBBMap[EHPadBB], BeginLabel, EndLabel); + MF.addInvoke(EHPadBB ? FuncInfo.MBBMap[EHPadBB] : nullptr, BeginLabel, + EndLabel); } return Chain; @@ -8021,10 +8021,11 @@ std::pair SelectionDAGBuilder::lowerInvokable(TargetLowering::CallLoweringInfo &CLI, - const BasicBlock *EHPadBB) { + const BasicBlock *EHPadBB, + bool EmitEHLabels) { MCSymbol *BeginLabel = nullptr; - if (EHPadBB) { + if (EmitEHLabels) { // Both PendingLoads and PendingExports must be flushed here; // this call might not return. (void)getRoot(); @@ -8052,9 +8053,8 @@ DAG.setRoot(Result.second); } - if (EHPadBB) { - DAG.setRoot(lowerEndEH(getRoot(), cast_or_null(CLI.CB), EHPadBB, - BeginLabel)); + if (EmitEHLabels) { + DAG.setRoot(lowerEndEH(getRoot(), CLI.CB, EHPadBB, BeginLabel)); } return Result; @@ -8074,6 +8074,10 @@ const Value *SwiftErrorVal = nullptr; const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + // We need EH_LABELs for unwindabort call instructions and when there's an + // EHPadBB. + bool EmitEHLabels = EHPadBB != nullptr || CB.isUnwindAbort(); + if (isTailCall) { // Avoid emitting tail calls in functions with the disable-tail-calls // attribute. @@ -8163,7 +8167,8 @@ .setIsPreallocated( CB.countOperandBundlesOfType(LLVMContext::OB_preallocated) != 0) .setCFIType(CFIType); - std::pair Result = lowerInvokable(CLI, EHPadBB); + std::pair Result = + lowerInvokable(CLI, EHPadBB, EmitEHLabels); if (Result.first.getNode()) { Result.first = lowerRangeToAssertZExt(DAG, CB, Result.first); @@ -9067,10 +9072,7 @@ // memory and is nonvolatile. SDValue Glue, Chain = (HasSideEffect) ? getRoot() : DAG.getRoot(); - bool EmitEHLabels = isa(Call); - if (EmitEHLabels) { - assert(EHPadBB && "InvokeInst must have an EHPadBB"); - } + bool EmitEHLabels = isa(Call) || Call.isUnwindAbort(); bool IsCallBr = isa(Call); if (IsCallBr || EmitEHLabels) { @@ -9556,7 +9558,7 @@ Chain = DAG.getNode(ISD::TokenFactor, getCurSDLoc(), MVT::Other, OutChains); if (EmitEHLabels) { - Chain = lowerEndEH(Chain, cast(&Call), EHPadBB, BeginLabel); + Chain = lowerEndEH(Chain, &Call, EHPadBB, BeginLabel); } // Only Update Root if inline assembly has a memory effect. @@ -9841,7 +9843,8 @@ TargetLowering::CallLoweringInfo CLI(DAG); populateCallLoweringInfo(CLI, &CB, NumMetaOpers, NumCallArgs, Callee, ReturnTy, true); - std::pair Result = lowerInvokable(CLI, EHPadBB); + std::pair Result = + lowerInvokable(CLI, EHPadBB, EHPadBB || CB.isUnwindAbort()); SDNode *CallEnd = Result.second.getNode(); if (HasDef && (CallEnd->getOpcode() == ISD::CopyFromReg)) diff --git a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp @@ -320,8 +320,8 @@ SelectionDAGBuilder::StatepointLoweringInfo &SI, SelectionDAGBuilder &Builder) { SDValue ReturnValue, CallEndVal; - std::tie(ReturnValue, CallEndVal) = - Builder.lowerInvokable(SI.CLI, SI.EHPadBB); + std::tie(ReturnValue, CallEndVal) = Builder.lowerInvokable( + SI.CLI, SI.EHPadBB, SI.EHPadBB /*FIXME: || IsUnwindAbort*/); SDNode *CallEnd = CallEndVal.getNode(); // Get a call instruction from the call sequence chain. Tail calls are not diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -1384,9 +1384,13 @@ } } -void WinEHFuncInfo::addIPToStateRange(const InvokeInst *II, - MCSymbol *InvokeBegin, +void WinEHFuncInfo::addIPToStateRange(const CallBase *CB, MCSymbol *InvokeBegin, MCSymbol *InvokeEnd) { + // TODO: implement support for 'call unwindabort' in WinEH + const InvokeInst *II = dyn_cast(CB); + if (!II) + return; + assert(InvokeStateMap.count(II) && "should get invoke with precomputed state"); LabelToStateMap[InvokeBegin] = std::make_pair(InvokeStateMap[II], InvokeEnd); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-tail-call.ll b/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-tail-call.ll --- a/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-tail-call.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-tail-call.ll @@ -2,6 +2,8 @@ ; RUN: llc %s -stop-after=irtranslator -verify-machineinstrs -mtriple aarch64-apple-darwin -global-isel -o - 2>&1 | FileCheck %s --check-prefix=DARWIN ; RUN: llc %s -stop-after=irtranslator -verify-machineinstrs -mtriple aarch64-windows -global-isel -o - 2>&1 | FileCheck %s --check-prefix=WINDOWS +declare i32 @__gxx_personality_v0(...) + declare void @simple_fn() define void @tail_call() { ; DARWIN-LABEL: name: tail_call @@ -14,6 +16,30 @@ ret void } +; We cannot tail-call when unwindabort is required. +define void @no_tail_call_unwindabort() personality ptr @__gxx_personality_v0 { + ; DARWIN-LABEL: name: no_tail_call_unwindabort + ; DARWIN: bb.1 (%ir-block.0): + ; DARWIN-NEXT: G_INVOKE_REGION_START + ; DARWIN-NEXT: EH_LABEL + ; DARWIN-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; DARWIN-NEXT: BL @simple_fn, csr_darwin_aarch64_aapcs, implicit-def $lr, implicit $sp + ; DARWIN-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; DARWIN-NEXT: EH_LABEL + ; DARWIN-NEXT: RET_ReallyLR + ; WINDOWS-LABEL: name: no_tail_call_unwindabort + ; WINDOWS: bb.1 (%ir-block.0): + ; WINDOWS-NEXT: G_INVOKE_REGION_START + ; WINDOWS-NEXT: EH_LABEL + ; WINDOWS-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; WINDOWS-NEXT: BL @simple_fn, csr_aarch64_aapcs, implicit-def $lr, implicit $sp + ; WINDOWS-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; WINDOWS-NEXT: EH_LABEL + ; WINDOWS-NEXT: RET_ReallyLR + tail call unwindabort void @simple_fn() + ret void +} + ; We should get a TCRETURNri here. ; FIXME: We don't need the COPY. define void @indirect_tail_call(ptr %func) { diff --git a/llvm/test/CodeGen/AArch64/call-unwindabort-codegen.ll b/llvm/test/CodeGen/AArch64/call-unwindabort-codegen.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/call-unwindabort-codegen.ll @@ -0,0 +1,29 @@ +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=0 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=1 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=0 -fast-isel=1 < %s | FileCheck %s + +declare void @throws() + +define dso_local void @test() personality ptr @__gxx_personality_v0 { +entry: +; CHECK-LABEL: test: +; CHECK: .Ltmp0: +; CHECK-NEXT: bl throws +; CHECK-NEXT: .Ltmp1: + + call unwindabort void @throws() + ret void +} + +declare dso_local i32 @__gxx_personality_v0(...) + +; Exception table generation around the inline assembly + +; CHECK-LABEL: GCC_except_table0: +; CHECK-NEXT: .Lexception0: +; CHECK-NEXT: .byte 255 // @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 // @TType Encoding = omit +; CHECK-NEXT: .byte 1 // Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 .Lcst_end0-.Lcst_begin0 +; CHECK-NEXT: .Lcst_begin0: +; CHECK-NEXT: .Lcst_end0: diff --git a/llvm/test/CodeGen/AArch64/unwindabort-inline-asm-codegen.ll b/llvm/test/CodeGen/AArch64/unwindabort-inline-asm-codegen.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/unwindabort-inline-asm-codegen.ll @@ -0,0 +1,29 @@ +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=0 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=1 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=aarch64-unknown-linux-gnu -global-isel=0 -fast-isel=1 < %s | FileCheck %s + +define dso_local void @test() personality ptr @__gxx_personality_v0 { +entry: +; CHECK-LABEL: test: +; CHECK: .Ltmp0: +; CHECK-NEXT: //APP +; CHECK-NEXT: nop +; CHECK-NEXT: //NO_APP +; CHECK-NEXT: .Ltmp1: + + call unwindabort void asm sideeffect unwind "nop", "~{dirflag},~{fpsr},~{flags}"() + ret void +} + +declare dso_local i32 @__gxx_personality_v0(...) + +; Exception table generation around the inline assembly + +; CHECK-LABEL: GCC_except_table0: +; CHECK-NEXT: .Lexception0: +; CHECK-NEXT: .byte 255 // @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 // @TType Encoding = omit +; CHECK-NEXT: .byte 1 // Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 .Lcst_end0-.Lcst_begin0 +; CHECK-NEXT: .Lcst_begin0: +; CHECK-NEXT: .Lcst_end0: diff --git a/llvm/test/CodeGen/X86/call-unwindabort-codegen.ll b/llvm/test/CodeGen/X86/call-unwindabort-codegen.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/call-unwindabort-codegen.ll @@ -0,0 +1,29 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -fast-isel=1 < %s | FileCheck %s + +declare void @throws() + +define dso_local void @test() personality ptr @__gxx_personality_v0 { +entry: +; CHECK-LABEL: test: +; CHECK: .Ltmp0: +; CHECK-NEXT: callq throws@PLT +; CHECK-NEXT: .Ltmp1: + + call unwindabort void @throws() + ret void +} + +declare dso_local i32 @__gxx_personality_v0(...) + +; Exception table generation around the inline assembly + + +; CHECK-LABEL: GCC_except_table0: +; CHECK-NEXT: .Lexception0: +; CHECK-NEXT: .byte 255 # @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 # @TType Encoding = omit +; CHECK-NEXT: .byte 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 .Lcst_end0-.Lcst_begin0 +; CHECK-NEXT: .Lcst_begin0: +; CHECK-NEXT: .Lcst_end0: diff --git a/llvm/test/CodeGen/X86/eh-callsites.ll b/llvm/test/CodeGen/X86/eh-callsites.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/eh-callsites.ll @@ -0,0 +1,184 @@ +; RUN: llc -mtriple=x86_64-linux-gnu -global-isel=0 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-linux-gnu -global-isel=1 -fast-isel=0 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-linux-gnu -global-isel=0 -fast-isel=1 < %s | FileCheck %s + +;; These tests verify that we construct proper LSDA call-site tables for the normal DWARF personality style. + +declare void @throw() +declare void @nothrow() #0 + +declare i32 @__gxx_personality_v0(...) + +;; A function with only unwindabort should generate an empty callsite +;; table in the LSDA. +; CHECK-LABEL: abort_only: +; CHECK: .cfi_personality 3, __gxx_personality_v0 +; CHECK: .cfi_lsda 3, [[EXTABLE:.Lexception[0-9]+]] +; CHECK: callq throw +; CHECK: [[EXTABLE]]: +; CHECK-NEXT: .byte 255 # @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 # @TType Encoding = omit +; CHECK-NEXT: .byte 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 [[CST_END:.Lcst_end[0-9]+]]-[[CST_BEGIN:.Lcst_begin[0-9]+]] +; CHECK-NEXT: [[CST_BEGIN]]: +; CHECK-NEXT: [[CST_END]]: +define void @abort_only() personality ptr @__gxx_personality_v0 { +entry: + call unwindabort void @throw() + unreachable +} + +;; A function with only a potentially-throwing calls should not +;; generate any LSDA at all. +; CHECK-LABEL: throwing_call_only: +; CHECK-NOT: .cfi_personality +; CHECK-NOT: .cfi_lsda +; CHECK: callq throw +define void @throwing_call_only() personality ptr @__gxx_personality_v0 { +entry: + call void @throw() + unreachable +} + +;; Here, we have some unwindabort calls, some plain calls that unwind +;; out of the function, and some interspersed calls to nounwind +;; functions. +;; - The potentially-unwinding callsite must be included in the +;; callsite table. +;; - The unwindabort callsite must NOT be included in the table. +;; - Nounwind callsites can be included into any region, whatever's convenient. + +; CHECK-LABEL: interspersed_nounwinds: +; CHECK: [[BEGIN:.Lfunc_begin[0-9]+]]: +; CHECK: .cfi_personality 3, __gxx_personality_v0 +; CHECK: .cfi_lsda 3, [[EXTABLE:.Lexception[0-9]+]] +; CHECK: [[TMP1:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[TMP2:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq nothrow +; CHECK-NEXT: [[TMP3:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[TMP4:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: callq nothrow +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[END:.Lfunc_end[0-9]+]]: +; CHECK: [[EXTABLE]] +; CHECK-NEXT: .byte 255 # @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 # @TType Encoding = omit +; CHECK-NEXT: .byte 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 [[CST_END:.Lcst_end[0-9]+]]-[[CST_BEGIN:.Lcst_begin[0-9]+]] +; CHECK-NEXT: [[CST_BEGIN]]: +; CHECK-NEXT: .uleb128 [[TMP4]]-[[BEGIN]] # >> Call Site 1 << +; CHECK-NEXT: .uleb128 [[END]]-[[TMP4]] # Call between [[TMP4]] and [[END]] +; CHECK-NEXT: .byte 0 # has no landing pad +; CHECK-NEXT: .byte 0 # On action: cleanup +; CHECK-NEXT: [[CST_END]]: + + +define void @interspersed_nounwinds() personality ptr @__gxx_personality_v0 { +entry: + call unwindabort void @throw() + call void @nothrow() #0 + call unwindabort void @throw() + + call void @throw() + call void @nothrow() #0 + call void @throw() + unreachable +} + +;; This tests the LSDA callsite and action maps resulting from various sorts of invoke landingpads are appropriate. +;; + +; CHECK-LABEL: invokes: +; CHECK: [[BEGIN:.Lfunc_begin[0-9]+]]: +; CHECK: .cfi_personality 3, __gxx_personality_v0 +; CHECK: .cfi_lsda 3, [[EXTABLE:.Lexception[0-9]+]] +; CHECK: [[TMP1:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[TMP2:.Ltmp[0-9]+]]: +; CHECK: [[TMP3:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[TMP4:.Ltmp[0-9]+]]: +; CHECK: [[TMP5:.Ltmp[0-9]+]]: +; CHECK-NEXT: callq throw +; CHECK-NEXT: [[TMP6:.Ltmp[0-9]+]]: +; CHECK: callq _Unwind_Resume +; CHECK: [[TMP7:.Ltmp[0-9]+]]: +; CHECK-NEXT: [[TMP8:.Ltmp[0-9]+]]: +; CHECK: callq _Unwind_Resume +; CHECK: [[END:.Lfunc_end[0-9]+]]: +; CHECK: [[EXTABLE]]: +; CHECK-NEXT: .byte 255 # @LPStart Encoding = omit +; CHECK-NEXT: .byte 3 # @TType Encoding = udata4 +; CHECK-NEXT: .uleb128 [[TTBASE:.Lttbase[0-9]+]]-[[TTBASEREF:.Lttbaseref[0-9]+]] +; CHECK-NEXT: [[TTBASEREF]]: +; CHECK-NEXT: .byte 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 [[CST_END:.Lcst_end[0-9]+]]-[[CST_BEGIN:.Lcst_begin[0-9]+]] +; CHECK-NEXT: [[CST_BEGIN]]: +; CHECK-NEXT: .uleb128 [[TMP1]]-[[BEGIN]] # >> Call Site 1 << +; CHECK-NEXT: .uleb128 [[TMP2]]-[[TMP1]] # Call between [[TMP1]] and [[TMP2]] +; CHECK-NEXT: .uleb128 {{.Ltmp[0-9]+}}-[[BEGIN]] # jumps to {{.Ltmp[0-9]+}} +; CHECK-NEXT: .byte 3 # On action: 2 +; CHECK-NEXT: .uleb128 [[TMP3]]-[[BEGIN]] # >> Call Site 2 << +; CHECK-NEXT: .uleb128 [[TMP4]]-[[TMP3]] # Call between [[TMP3]] and [[TMP4]] +; CHECK-NEXT: .uleb128 {{.Ltmp[0-9]+}}-[[BEGIN]] # jumps to {{.Ltmp[0-9]+}} +; CHECK-NEXT: .byte 0 # On action: cleanup +; CHECK-NEXT: .uleb128 [[TMP5]]-[[BEGIN]] # >> Call Site 3 << +; CHECK-NEXT: .uleb128 [[TMP6]]-[[TMP5]] # Call between [[TMP5]] and [[TMP6]] +; CHECK-NEXT: .uleb128 {{.Ltmp[0-9]+}}-[[BEGIN]] # jumps to {{.Ltmp[0-9]+}} +; CHECK-NEXT: .byte 5 # On action: 3 +; CHECK-NEXT: .uleb128 [[TMP6]]-[[BEGIN]] # >> Call Site 4 << +; CHECK-NEXT: .uleb128 [[TMP8]]-[[TMP6]] # Call between [[TMP6]] and [[TMP8]] +; CHECK-NEXT: .byte 0 # has no landing pad +; CHECK-NEXT: .byte 0 # On action: cleanup +; CHECK-NEXT: [[CST_END]]: +; CHECK-NEXT: .byte 0 # >> Action Record 1 << +; CHECK-NEXT: # Cleanup +; CHECK-NEXT: .byte 0 # No further actions +; CHECK-NEXT: .byte 1 # >> Action Record 2 << +; CHECK-NEXT: # Catch TypeInfo 1 +; CHECK-NEXT: .byte 125 # Continue to action 1 +; CHECK-NEXT: .byte 1 # >> Action Record 3 << +; CHECK-NEXT: # Catch TypeInfo 1 +; CHECK-NEXT: .byte 0 # No further actions +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: # >> Catch TypeInfos << +; CHECK-NEXT: .long 0 # TypeInfo 1 +; CHECK-NEXT: [[TTBASE]]: + +define void @invokes(i8 %x) personality ptr @__gxx_personality_v0 { +entry: + switch i8 %x, label %one [ i8 0, label %two + i8 1, label %three ] +one: + invoke void @throw() + to label %cont unwind label %catch +two: + invoke void @throw() + to label %cont unwind label %cleanup +three: + invoke void @throw() + to label %cont unwind label %catch_cleanup + +cont: + ret void + +catch: + %0 = landingpad { ptr, i32 } + catch ptr null + ret void + +cleanup: + %1 = landingpad { ptr, i32 } + cleanup + resume { ptr, i32 } %1 +catch_cleanup: + %2 = landingpad { ptr, i32 } + cleanup + catch ptr null + resume unwindabort { ptr, i32 } %2 +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/X86/indirect-branch-tracking-eh2.ll b/llvm/test/CodeGen/X86/indirect-branch-tracking-eh2.ll --- a/llvm/test/CodeGen/X86/indirect-branch-tracking-eh2.ll +++ b/llvm/test/CodeGen/X86/indirect-branch-tracking-eh2.ll @@ -9,7 +9,7 @@ ;SJLJ-NEXT: endbr64 ;SJLJ-NEXT: pushq %rbp ;SJLJ: callq _Unwind_SjLj_Register -;SJLJ-NEXT: .Ltmp0: +;SJLJ: .Ltmp0: ;SJLJ-NEXT: callq _Z3foov ;SJLJ-NEXT: .Ltmp1: ;SJLJ-NEXT: # %bb.1: # %invoke.cont diff --git a/llvm/test/CodeGen/X86/unwind-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/unwind-inline-asm-codegen.ll --- a/llvm/test/CodeGen/X86/unwind-inline-asm-codegen.ll +++ b/llvm/test/CodeGen/X86/unwind-inline-asm-codegen.ll @@ -12,10 +12,10 @@ define dso_local void @test() personality ptr @__gxx_personality_v0 { entry: - -; CHECK-LABEL: .Ltmp0: +; CHECK-LABEL: test: +; CHECK: .Ltmp0: ; CHECK: callq trap -; CHECK-LABEL: .Ltmp1: +; CHECK: .Ltmp1: invoke void asm sideeffect unwind "call trap", "~{dirflag},~{fpsr},~{flags}"() to label %invoke.cont unwind label %lpad diff --git a/llvm/test/CodeGen/X86/unwindabort-inline-asm-codegen.ll b/llvm/test/CodeGen/X86/unwindabort-inline-asm-codegen.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/unwindabort-inline-asm-codegen.ll @@ -0,0 +1,31 @@ +; RUN: llc -fast-isel=0 < %s | FileCheck %s +; RUN: llc -fast-isel=1 < %s | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local void @test() personality ptr @__gxx_personality_v0 { +entry: +; CHECK-LABEL: test: +; CHECK: .Ltmp0: +; CHECK-NEXT: #APP +; CHECK-NEXT: nop +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: .Ltmp1: + + call unwindabort void asm sideeffect unwind "nop", "~{dirflag},~{fpsr},~{flags}"() + ret void +} + +declare dso_local i32 @__gxx_personality_v0(...) + +; Exception table generation around the inline assembly. + +; CHECK-LABEL: GCC_except_table0: +; CHECK-NEXT: .Lexception0: +; CHECK-NEXT: .byte 255 # @LPStart Encoding = omit +; CHECK-NEXT: .byte 255 # @TType Encoding = omit +; CHECK-NEXT: .byte 1 # Call site Encoding = uleb128 +; CHECK-NEXT: .uleb128 .Lcst_end0-.Lcst_begin0 +; CHECK-NEXT: .Lcst_begin0: +; CHECK-NEXT: .Lcst_end0: