diff --git a/llvm/docs/ExceptionHandling.rst b/llvm/docs/ExceptionHandling.rst --- a/llvm/docs/ExceptionHandling.rst +++ b/llvm/docs/ExceptionHandling.rst @@ -293,6 +293,28 @@ such ``landingpad`` instructions due to inlining creating nested exception handling scopes. +Noexcept +-------- + +C++11 added the `noexcept` qualifier on functions, which specifies that if any +exceptions are thrown out of the function, the program should abort. Unlike +dynamic exception specifiers (e.g. `throw(ExcType...)`), `noexcept` does not +require the stack to be fully unwound (and all destructors called) along the way +to program termination. This allowance is specified in order to allow for more +efficient code generation, and more optimization opportunities. + +In order to efficiently support these semantics, LLVM's :ref:`i_call` has an +`unwindabort` flag. This flag causes construction of the unwind tables to mark +the callsite in such a way that the personality function knows to call +std::terminate if an unwind is attempted through this stack frame -- without +requiring the generation of a landingpad containing an explicit terminate call, +and a corresponding usage of ``invoke`` for all calls within the function. + +Furthermore, this flag allows the inliner to delete a ``cleanuppad`` or a +``landingpad`` with only a *cleanup* clause which is inlined through an +``unwindabort``. (It's "as if" the unwinder only partially unwound before +terminating the program, which is permitted.) + Restrictions ------------ diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -12055,7 +12055,7 @@ :: - = [tail | musttail | notail ] call [fast-math flags] [cconv] [ret attrs] [addrspace()] + = [tail | musttail | notail ] call [unwindabort] [fast-math flags] [cconv] [ret attrs] [addrspace()] | () [fn attrs] [ operand bundles ] Overview: @@ -12100,6 +12100,7 @@ non-varargs function to the appropriate varargs type is legal so long as the non-varargs prefixes obey the other rules. - The return type must not undergo automatic conversion to an `sret` pointer. + - The call cannot be 'unwindabort'. In addition, if the calling convention is not `swifttailcc` or `tailcc`: @@ -12130,6 +12131,20 @@ ``tail`` or ``musttail`` markers to the call. It is used to prevent tail call optimization from being performed on the call. +#. The optional '``unwindabort``' marker is used by `LLVM's exception + handling system `_ to specify that + if the call stack is being unwound due to an exception, the + :ref:`personality function ` should abort the + process instead of continuing to unwind to the caller. + + It is unspecified whether any intermediate *cleanup* + :ref:`landingpad ` or :ref:`cleanuppad + ` handlers are called during the unwind process, when + unwinding into an ``unwindabort``. (The compiler may omit any + cleanup which would resume execution only through ``unwindabort``, + or the personality function may simply not call cleanups once it + discovers it needs to abort.) + #. The optional ``fast-math flags`` marker indicates that the call has one or more :ref:`fast-math flags `, which are optimization hints to enable otherwise unsafe floating-point optimizations. Fast-math flags are only valid diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -84,6 +84,7 @@ kw_tail, kw_musttail, kw_notail, + kw_unwindabort, kw_target, kw_triple, kw_source_filename, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -527,7 +527,8 @@ CALL_MUSTTAIL = 14, CALL_EXPLICIT_TYPE = 15, CALL_NOTAIL = 16, - CALL_FMF = 17 // Call has optional fast-math-flags. + CALL_FMF = 17, // Call has optional fast-math-flags. + CALL_UNWINDABORT = 18 }; // The function body block (FUNCTION_BLOCK_ID) describes function bodies. It diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1440,6 +1440,9 @@ /// Tests if this call site is marked as a tail call. bool isTailCall() const; + /// Tests if this call site is marked 'unwindabort'. + bool isUnwindAbort() const; + /// Returns the intrinsic ID of the intrinsic called or /// Intrinsic::not_intrinsic if the called function is not an intrinsic, or if /// this is an indirect call. diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -1653,6 +1653,11 @@ Bitfield::areContiguous(), "Bitfields must be contiguous"); + using UnwindAbortField = BoolBitfieldElementT; + static_assert( + Bitfield::areContiguous(), + "Bitfields must be contiguous"); + TailCallKind getTailCallKind() const { return getSubclassData(); } @@ -1674,6 +1679,11 @@ setTailCallKind(IsTc ? TCK_Tail : TCK_None); } + bool isUnwindAbort() const { return getSubclassData(); } + void setUnwindAbort(bool IsUA = true) { + setSubclassData(IsUA); + } + /// Return true if the call can return twice bool canReturnTwice() const { return hasFnAttr(Attribute::ReturnsTwice); } void setCanReturnTwice() { addFnAttr(Attribute::ReturnsTwice); } diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -538,6 +538,7 @@ KEYWORD(tail); KEYWORD(musttail); KEYWORD(notail); + KEYWORD(unwindabort); KEYWORD(target); KEYWORD(triple); KEYWORD(source_filename); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -7419,14 +7419,16 @@ } /// parseCall -/// ::= 'call' OptionalFastMathFlags OptionalCallingConv +/// ::= 'call' 'unwindabort'? OptionalFastMathFlags OptionalCallingConv /// OptionalAttrs Type Value ParameterList OptionalAttrs -/// ::= 'tail' 'call' OptionalFastMathFlags OptionalCallingConv -/// OptionalAttrs Type Value ParameterList OptionalAttrs -/// ::= 'musttail' 'call' OptionalFastMathFlags OptionalCallingConv -/// OptionalAttrs Type Value ParameterList OptionalAttrs -/// ::= 'notail' 'call' OptionalFastMathFlags OptionalCallingConv +/// ::= 'tail' 'call' 'unwindabort'? OptionalFastMathFlags OptionalCallingConv /// OptionalAttrs Type Value ParameterList OptionalAttrs +/// ::= 'musttail' 'call' 'unwindabort'? OptionalFastMathFlags +/// OptionalCallingConv OptionalAttrs Type Value ParameterList +/// OptionalAttrs +/// ::= 'notail' 'call' 'unwindabort'? OptionalFastMathFlags +/// OptionalCallingConv OptionalAttrs Type Value ParameterList +/// OptionalAttrs bool LLParser::parseCall(Instruction *&Inst, PerFunctionState &PFS, CallInst::TailCallKind TCK) { AttrBuilder RetAttrs(M->getContext()), FnAttrs(M->getContext()); @@ -7440,12 +7442,18 @@ SmallVector ArgList; SmallVector BundleList; LocTy CallLoc = Lex.getLoc(); + bool UnwindAbort = false; if (TCK != CallInst::TCK_None && parseToken(lltok::kw_call, "expected 'tail call', 'musttail call', or 'notail call'")) return true; + if (Lex.getKind() == lltok::kw_unwindabort) { + Lex.Lex(); + UnwindAbort = true; + } + FastMathFlags FMF = EatFastMathFlagsIfPresent(); if (parseOptionalCallingConv(CC) || parseOptionalReturnAttrs(RetAttrs) || @@ -7507,6 +7515,7 @@ CallInst *CI = CallInst::Create(Ty, Callee, Args, BundleList); CI->setTailCallKind(TCK); + CI->setUnwindAbort(UnwindAbort); CI->setCallingConv(CC); if (FMF.any()) { if (!isa(CI)) { diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -6367,6 +6367,8 @@ ResTypeID = getContainedTypeID(FTyID); OperandBundles.clear(); InstructionList.push_back(I); + if (CCInfo & (1 << bitc::CALL_UNWINDABORT)) + cast(I)->setUnwindAbort(true); cast(I)->setCallingConv( static_cast((0x7ff & CCInfo) >> bitc::CALL_CCONV)); CallInst::TailCallKind TCK = CallInst::TCK_None; diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3213,7 +3213,8 @@ unsigned(CI.isMustTailCall()) << bitc::CALL_MUSTTAIL | 1 << bitc::CALL_EXPLICIT_TYPE | unsigned(CI.isNoTailCall()) << bitc::CALL_NOTAIL | - unsigned(Flags != 0) << bitc::CALL_FMF); + unsigned(Flags != 0) << bitc::CALL_FMF | + unsigned(CI.isUnwindAbort()) << bitc::CALL_UNWINDABORT); if (Flags != 0) Vals.push_back(Flags); diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -4211,6 +4211,9 @@ else Out << "to caller"; } else if (const CallInst *CI = dyn_cast(&I)) { + if (CI->isUnwindAbort()) + Out << " unwindabort"; + // Print the calling convention being used. if (CI->getCallingConv() != CallingConv::C) { Out << " "; diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -319,6 +319,12 @@ return false; } +bool CallBase::isUnwindAbort() const { + if (auto *CI = dyn_cast(this)) + return CI->isUnwindAbort(); + return false; +} + Intrinsic::ID CallBase::getIntrinsicID() const { if (auto *F = getCalledFunction()) return F->getIntrinsicID(); @@ -694,6 +700,7 @@ : CallBase(CI.Attrs, CI.FTy, CI.getType(), Instruction::Call, OperandTraits::op_end(this) - CI.getNumOperands(), CI.getNumOperands()) { + setUnwindAbort(CI.isUnwindAbort()); setTailCallKind(CI.getTailCallKind()); setCallingConv(CI.getCallingConv()); @@ -709,6 +716,7 @@ auto *NewCI = CallInst::Create(CI->getFunctionType(), CI->getCalledOperand(), Args, OpB, CI->getName(), InsertPt); + NewCI->setUnwindAbort(CI->isUnwindAbort()); NewCI->setTailCallKind(CI->getTailCallKind()); NewCI->setCallingConv(CI->getCallingConv()); NewCI->SubclassOptionalData = CI->SubclassOptionalData; diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -3751,10 +3751,12 @@ void Verifier::verifyMustTailCall(CallInst &CI) { Check(!CI.isInlineAsm(), "cannot use musttail call with inline asm", &CI); + Check(!CI.isUnwindAbort(), "cannot use musttail with unwindabort", &CI); Function *F = CI.getParent()->getParent(); FunctionType *CallerTy = F->getFunctionType(); FunctionType *CalleeTy = CI.getFunctionType(); + Check(CallerTy->isVarArg() == CalleeTy->isVarArg(), "cannot guarantee tail call due to mismatched varargs", &CI); Check(isTypeCongruent(CallerTy->getReturnType(), CalleeTy->getReturnType()), @@ -3839,6 +3841,12 @@ void Verifier::visitCallInst(CallInst &CI) { visitCallBase(CI); + if (CI.isUnwindAbort()) { + Check(CI.getFunction()->hasPersonalityFn(), + "Call with unwindabort needs to be in a function with a personality.", + &CI); + } + if (CI.isMustTailCall()) verifyMustTailCall(CI); } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1590,6 +1590,12 @@ ret void } +define void @instructions.call_unwindabort() personality i32 -2 { + call unwindabort void @f1() + ; CHECK: call unwindabort void @f1() + ret void +} + define void @instructions.landingpad() personality i32 -2 { invoke void @llvm.donothing() to label %proceed unwind label %catch1 invoke void @llvm.donothing() to label %proceed unwind label %catch2 diff --git a/llvm/test/Verifier/musttail-invalid.ll b/llvm/test/Verifier/musttail-invalid.ll --- a/llvm/test/Verifier/musttail-invalid.ll +++ b/llvm/test/Verifier/musttail-invalid.ll @@ -80,3 +80,11 @@ musttail call void asm "ret", ""() ret void } + +declare i32 @testfn() +declare void @person() +define i32 @unwindabort() personality ptr @person { +; CHECK: cannot use musttail with unwindabort + %v = musttail call unwindabort i32 @testfn() + ret i32 %v +} diff --git a/llvm/test/Verifier/unwindabort-personality.ll b/llvm/test/Verifier/unwindabort-personality.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/unwindabort-personality.ll @@ -0,0 +1,9 @@ +; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s + +declare void @fn() +define void @test1() { +entry: + call unwindabort void @fn() + ret void +; CHECK: Call with unwindabort needs to be in a function with a personality. +}