[SEH] Re-land r234532, but use internal linkage for all SEH helpers

Even though these symbols are in a comdat group, the Microsoft linker
really wants them to have internal linkage.

I'm planning to tweak the mangling in a follow-up change. This is a
straight revert with a 1-line fix.

llvm-svn: 234613
This commit is contained in:
Reid Kleckner
2015-04-10 17:34:52 +00:00
parent 0aa128e219
commit 11859afd5f
10 changed files with 362 additions and 371 deletions

View File

@@ -135,6 +135,9 @@ public:
virtual void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, virtual void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
raw_ostream &Out) = 0; raw_ostream &Out) = 0;
virtual void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl,
raw_ostream &Out) = 0;
/// Generates a unique string for an externally visible type for use with TBAA /// Generates a unique string for an externally visible type for use with TBAA
/// or type uniquing. /// or type uniquing.
/// TODO: Extend this to internal types by generating names that are unique /// TODO: Extend this to internal types by generating names that are unique

View File

@@ -166,6 +166,8 @@ public:
raw_ostream &Out) override; raw_ostream &Out) override;
void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
raw_ostream &Out) override; raw_ostream &Out) override;
void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl,
raw_ostream &Out) override;
void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &) override; void mangleItaniumThreadLocalInit(const VarDecl *D, raw_ostream &) override;
void mangleItaniumThreadLocalWrapper(const VarDecl *D, void mangleItaniumThreadLocalWrapper(const VarDecl *D,
raw_ostream &) override; raw_ostream &) override;
@@ -3967,6 +3969,16 @@ void ItaniumMangleContextImpl::mangleSEHFilterExpression(
Mangler.getStream() << EnclosingDecl->getName(); Mangler.getStream() << EnclosingDecl->getName();
} }
void ItaniumMangleContextImpl::mangleSEHFinallyBlock(
const NamedDecl *EnclosingDecl, raw_ostream &Out) {
CXXNameMangler Mangler(*this, Out);
Mangler.getStream() << "__fin_";
if (shouldMangleDeclName(EnclosingDecl))
Mangler.mangle(EnclosingDecl);
else
Mangler.getStream() << EnclosingDecl->getName();
}
void ItaniumMangleContextImpl::mangleItaniumThreadLocalInit(const VarDecl *D, void ItaniumMangleContextImpl::mangleItaniumThreadLocalInit(const VarDecl *D,
raw_ostream &Out) { raw_ostream &Out) {
// <special-name> ::= TH <object name> // <special-name> ::= TH <object name>

View File

@@ -94,6 +94,7 @@ class MicrosoftMangleContextImpl : public MicrosoftMangleContext {
llvm::DenseMap<const NamedDecl *, unsigned> Uniquifier; llvm::DenseMap<const NamedDecl *, unsigned> Uniquifier;
llvm::DenseMap<const CXXRecordDecl *, unsigned> LambdaIds; llvm::DenseMap<const CXXRecordDecl *, unsigned> LambdaIds;
llvm::DenseMap<const NamedDecl *, unsigned> SEHFilterIds; llvm::DenseMap<const NamedDecl *, unsigned> SEHFilterIds;
llvm::DenseMap<const NamedDecl *, unsigned> SEHFinallyIds;
public: public:
MicrosoftMangleContextImpl(ASTContext &Context, DiagnosticsEngine &Diags) MicrosoftMangleContextImpl(ASTContext &Context, DiagnosticsEngine &Diags)
@@ -151,6 +152,8 @@ public:
raw_ostream &Out) override; raw_ostream &Out) override;
void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl,
raw_ostream &Out) override; raw_ostream &Out) override;
void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl,
raw_ostream &Out) override;
void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override; void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override;
void mangleCXXVTableBitSet(const CXXRecordDecl *RD, void mangleCXXVTableBitSet(const CXXRecordDecl *RD,
raw_ostream &Out) override; raw_ostream &Out) override;
@@ -2469,6 +2472,17 @@ void MicrosoftMangleContextImpl::mangleSEHFilterExpression(
Mangler.mangleName(EnclosingDecl); Mangler.mangleName(EnclosingDecl);
} }
void MicrosoftMangleContextImpl::mangleSEHFinallyBlock(
const NamedDecl *EnclosingDecl, raw_ostream &Out) {
MicrosoftCXXNameMangler Mangler(*this, Out);
// The function body is in the same comdat as the function with the handler,
// so the numbering here doesn't have to be the same across TUs.
//
// <mangled-name> ::= ?fin$ <filter-number> @0
Mangler.getStream() << "\01?fin$" << SEHFinallyIds[EnclosingDecl]++ << "@0@";
Mangler.mangleName(EnclosingDecl);
}
void MicrosoftMangleContextImpl::mangleTypeName(QualType T, raw_ostream &Out) { void MicrosoftMangleContextImpl::mangleTypeName(QualType T, raw_ostream &Out) {
// This is just a made up unique string for the purposes of tbaa. undname // This is just a made up unique string for the purposes of tbaa. undname
// does *not* know how to demangle it. // does *not* know how to demangle it.

View File

@@ -23,6 +23,7 @@
#include "llvm/IR/CallSite.h" #include "llvm/IR/CallSite.h"
#include "llvm/IR/Intrinsics.h" #include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang; using namespace clang;
using namespace CodeGen; using namespace CodeGen;
@@ -408,13 +409,6 @@ llvm::Value *CodeGenFunction::getSelectorFromSlot() {
return Builder.CreateLoad(getEHSelectorSlot(), "sel"); return Builder.CreateLoad(getEHSelectorSlot(), "sel");
} }
llvm::Value *CodeGenFunction::getAbnormalTerminationSlot() {
if (!AbnormalTerminationSlot)
AbnormalTerminationSlot =
CreateTempAlloca(Int8Ty, "abnormal.termination.slot");
return AbnormalTerminationSlot;
}
void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E, void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E,
bool KeepInsertionPoint) { bool KeepInsertionPoint) {
if (const Expr *SubExpr = E->getSubExpr()) { if (const Expr *SubExpr = E->getSubExpr()) {
@@ -1287,8 +1281,7 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
return; return;
} }
SEHFinallyInfo FI; EnterSEHTryStmt(S);
EnterSEHTryStmt(S, FI);
{ {
JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave"); JumpDest TryExit = getJumpDestInCurrentScope("__try.__leave");
@@ -1301,42 +1294,36 @@ void CodeGenFunction::EmitSEHTryStmt(const SEHTryStmt &S) {
else else
delete TryExit.getBlock(); delete TryExit.getBlock();
} }
ExitSEHTryStmt(S, FI); ExitSEHTryStmt(S);
} }
namespace { namespace {
struct PerformSEHFinally : EHScopeStack::Cleanup { struct PerformSEHFinally : EHScopeStack::Cleanup {
CodeGenFunction::SEHFinallyInfo *FI; llvm::Function *OutlinedFinally;
PerformSEHFinally(CodeGenFunction::SEHFinallyInfo *FI) : FI(FI) {} PerformSEHFinally(llvm::Function *OutlinedFinally)
: OutlinedFinally(OutlinedFinally) {}
void Emit(CodeGenFunction &CGF, Flags F) override { void Emit(CodeGenFunction &CGF, Flags F) override {
// Cleanups are emitted at most twice: once for normal control flow and once ASTContext &Context = CGF.getContext();
// for exception control flow. Branch into the finally block, and remember QualType ArgTys[2] = {Context.BoolTy, Context.VoidPtrTy};
// the continuation block so we can branch out later. FunctionProtoType::ExtProtoInfo EPI;
if (!FI->FinallyBB) { const auto *FTP = cast<FunctionType>(
FI->FinallyBB = CGF.createBasicBlock("__finally"); Context.getFunctionType(Context.VoidTy, ArgTys, EPI));
FI->FinallyBB->insertInto(CGF.CurFn);
FI->FinallyBB->moveAfter(CGF.Builder.GetInsertBlock());
}
// Set the termination status and branch in. CallArgList Args;
CGF.Builder.CreateStore( llvm::Value *IsForEH =
llvm::ConstantInt::get(CGF.Int8Ty, F.isForEHCleanup()), llvm::ConstantInt::get(CGF.ConvertType(ArgTys[0]), F.isForEHCleanup());
CGF.getAbnormalTerminationSlot()); Args.add(RValue::get(IsForEH), ArgTys[0]);
CGF.Builder.CreateBr(FI->FinallyBB);
// Create a continuation block for normal or exceptional control. CodeGenModule &CGM = CGF.CGM;
if (F.isForEHCleanup()) { llvm::Value *Zero = llvm::ConstantInt::get(CGM.Int32Ty, 0);
assert(!FI->ResumeBB && "double emission for EH"); llvm::Value *FrameAddr = CGM.getIntrinsic(llvm::Intrinsic::frameaddress);
FI->ResumeBB = CGF.createBasicBlock("__finally.resume"); llvm::Value *FP = CGF.Builder.CreateCall(FrameAddr, Zero);
CGF.EmitBlock(FI->ResumeBB); Args.add(RValue::get(FP), ArgTys[1]);
} else {
assert(F.isForNormalCleanup() && !FI->ContBB && "double normal emission"); const CGFunctionInfo &FnInfo =
FI->ContBB = CGF.createBasicBlock("__finally.cont"); CGM.getTypes().arrangeFreeFunctionCall(Args, FTP, /*chainCall=*/false);
CGF.EmitBlock(FI->ContBB); CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args);
// Try to keep source order.
FI->ContBB->moveAfter(FI->FinallyBB);
}
} }
}; };
} }
@@ -1354,7 +1341,8 @@ struct CaptureFinder : ConstStmtVisitor<CaptureFinder> {
// See if this is a capture, then recurse. // See if this is a capture, then recurse.
ConstStmtVisitor<CaptureFinder>::Visit(S); ConstStmtVisitor<CaptureFinder>::Visit(S);
for (const Stmt *Child : S->children()) for (const Stmt *Child : S->children())
Visit(Child); if (Child)
Visit(Child);
} }
void VisitDeclRefExpr(const DeclRefExpr *E) { void VisitDeclRefExpr(const DeclRefExpr *E) {
@@ -1403,12 +1391,16 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
CGM.ErrorUnsupported(VD, "VLA captured by SEH"); CGM.ErrorUnsupported(VD, "VLA captured by SEH");
continue; continue;
} }
assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) && assert((isa<ImplicitParamDecl>(VD) || VD->isLocalVarDeclOrParm()) &&
"captured non-local variable"); "captured non-local variable");
llvm::Value *ParentVar = ParentCGF.LocalDeclMap[VD]; // If this decl hasn't been declared yet, it will be declared in the
assert(ParentVar && "capture was not a local decl"); // OutlinedStmt.
auto I = ParentCGF.LocalDeclMap.find(VD);
if (I == ParentCGF.LocalDeclMap.end())
continue;
llvm::Value *ParentVar = I->second;
llvm::CallInst *RecoverCall = nullptr; llvm::CallInst *RecoverCall = nullptr;
CGBuilderTy Builder(AllocaInsertPt); CGBuilderTy Builder(AllocaInsertPt);
if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) { if (auto *ParentAlloca = dyn_cast<llvm::AllocaInst>(ParentVar)) {
@@ -1445,47 +1437,24 @@ void CodeGenFunction::EmitCapturedLocals(CodeGenFunction &ParentCGF,
} }
} }
/// Create a stub filter function that will ultimately hold the code of the /// Arrange a function prototype that can be called by Windows exception
/// filter expression. The EH preparation passes in LLVM will outline the code /// handling personalities. On Win64, the prototype looks like:
/// from the main function body into this stub. /// RetTy func(void *EHPtrs, void *ParentFP);
llvm::Function * void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF,
CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, StringRef Name, QualType RetTy,
const SEHExceptStmt &Except) { FunctionArgList &Args,
const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl; const Stmt *OutlinedStmt) {
llvm::Function *ParentFn = ParentCGF.CurFn; llvm::Function *ParentFn = ParentCGF.CurFn;
Expr *FilterExpr = Except.getFilterExpr();
// Get the mangled function name.
SmallString<128> Name;
{
llvm::raw_svector_ostream OS(Name);
const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl);
assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH");
CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS);
}
// Arrange a function with the declaration:
// int filt(EXCEPTION_POINTERS *exception_pointers, void *frame_pointer)
QualType RetTy = getContext().IntTy;
FunctionArgList Args;
SEHPointersDecl = ImplicitParamDecl::Create(
getContext(), nullptr, FilterExpr->getLocStart(),
&getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy);
Args.push_back(SEHPointersDecl);
Args.push_back(ImplicitParamDecl::Create(
getContext(), nullptr, FilterExpr->getLocStart(),
&getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration(
RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false); RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false);
llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo); llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo);
llvm::Function *Fn = llvm::Function::Create(FnTy, ParentFn->getLinkage(), llvm::Function *Fn = llvm::Function::Create(
Name.str(), &CGM.getModule()); FnTy, llvm::GlobalValue::InternalLinkage, Name.str(), &CGM.getModule());
// The filter is either in the same comdat as the function, or it's internal. // The filter is either in the same comdat as the function, or it's internal.
if (llvm::Comdat *C = ParentFn->getComdat()) { if (llvm::Comdat *C = ParentFn->getComdat()) {
Fn->setComdat(C); Fn->setComdat(C);
} else if (ParentFn->hasWeakLinkage() || ParentFn->hasLinkOnceLinkage()) { } else if (ParentFn->hasWeakLinkage() || ParentFn->hasLinkOnceLinkage()) {
// FIXME: Unreachable with Rafael's changes?
llvm::Comdat *C = CGM.getModule().getOrInsertComdat(ParentFn->getName()); llvm::Comdat *C = CGM.getModule().getOrInsertComdat(ParentFn->getName());
ParentFn->setComdat(C); ParentFn->setComdat(C);
Fn->setComdat(C); Fn->setComdat(C);
@@ -1493,14 +1462,55 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
Fn->setLinkage(llvm::GlobalValue::InternalLinkage); Fn->setLinkage(llvm::GlobalValue::InternalLinkage);
} }
StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args, IsOutlinedSEHHelper = true;
FilterExpr->getLocStart(), FilterExpr->getLocStart());
EmitSEHExceptionCodeSave(); StartFunction(GlobalDecl(), RetTy, Fn, FnInfo, Args,
OutlinedStmt->getLocStart(), OutlinedStmt->getLocStart());
CGM.SetLLVMFunctionAttributes(nullptr, FnInfo, CurFn);
auto AI = Fn->arg_begin(); auto AI = Fn->arg_begin();
++AI; ++AI;
EmitCapturedLocals(ParentCGF, FilterExpr, &*AI); EmitCapturedLocals(ParentCGF, OutlinedStmt, &*AI);
}
/// Create a stub filter function that will ultimately hold the code of the
/// filter expression. The EH preparation passes in LLVM will outline the code
/// from the main function body into this stub.
llvm::Function *
CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
const SEHExceptStmt &Except) {
const Expr *FilterExpr = Except.getFilterExpr();
SourceLocation StartLoc = FilterExpr->getLocStart();
SEHPointersDecl = ImplicitParamDecl::Create(
getContext(), nullptr, StartLoc,
&getContext().Idents.get("exception_pointers"), getContext().VoidPtrTy);
FunctionArgList Args;
Args.push_back(SEHPointersDecl);
Args.push_back(ImplicitParamDecl::Create(
getContext(), nullptr, StartLoc,
&getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
// Get the mangled function name.
SmallString<128> Name;
{
llvm::raw_svector_ostream OS(Name);
const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl;
const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl);
assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH");
CGM.getCXXABI().getMangleContext().mangleSEHFilterExpression(Parent, OS);
}
startOutlinedSEHHelper(ParentCGF, Name, getContext().IntTy, Args, FilterExpr);
// Mark finally block calls as nounwind and noinline to make LLVM's job a
// little easier.
// FIXME: Remove these restrictions in the future.
CurFn->addFnAttr(llvm::Attribute::NoUnwind);
CurFn->addFnAttr(llvm::Attribute::NoInline);
EmitSEHExceptionCodeSave();
// Emit the original filter expression, convert to i32, and return. // Emit the original filter expression, convert to i32, and return.
llvm::Value *R = EmitScalarExpr(FilterExpr); llvm::Value *R = EmitScalarExpr(FilterExpr);
@@ -1510,7 +1520,42 @@ CodeGenFunction::GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
FinishFunction(FilterExpr->getLocEnd()); FinishFunction(FilterExpr->getLocEnd());
return Fn; return CurFn;
}
llvm::Function *
CodeGenFunction::GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF,
const SEHFinallyStmt &Finally) {
const Stmt *FinallyBlock = Finally.getBlock();
SourceLocation StartLoc = FinallyBlock->getLocStart();
FunctionArgList Args;
Args.push_back(ImplicitParamDecl::Create(
getContext(), nullptr, StartLoc,
&getContext().Idents.get("abnormal_termination"), getContext().BoolTy));
Args.push_back(ImplicitParamDecl::Create(
getContext(), nullptr, StartLoc,
&getContext().Idents.get("frame_pointer"), getContext().VoidPtrTy));
// Get the mangled function name.
SmallString<128> Name;
{
llvm::raw_svector_ostream OS(Name);
const Decl *ParentCodeDecl = ParentCGF.CurCodeDecl;
const NamedDecl *Parent = dyn_cast_or_null<NamedDecl>(ParentCodeDecl);
assert(Parent && "FIXME: handle unnamed decls (lambdas, blocks) with SEH");
CGM.getCXXABI().getMangleContext().mangleSEHFinallyBlock(Parent, OS);
}
startOutlinedSEHHelper(ParentCGF, Name, getContext().VoidTy, Args,
FinallyBlock);
// Emit the original filter expression, convert to i32, and return.
EmitStmt(FinallyBlock);
FinishFunction(FinallyBlock->getLocEnd());
return CurFn;
} }
void CodeGenFunction::EmitSEHExceptionCodeSave() { void CodeGenFunction::EmitSEHExceptionCodeSave() {
@@ -1554,21 +1599,24 @@ llvm::Value *CodeGenFunction::EmitSEHExceptionCode() {
} }
llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() { llvm::Value *CodeGenFunction::EmitSEHAbnormalTermination() {
// Load from the abnormal termination slot. It will be uninitialized outside // Abnormal termination is just the first parameter to the outlined finally
// of __finally blocks, which we should warn or error on. // helper.
llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot()); auto AI = CurFn->arg_begin();
return Builder.CreateZExt(IsEH, Int32Ty); return Builder.CreateZExt(&*AI, Int32Ty);
} }
void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S) {
if (S.getFinallyHandler()) { CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) {
// Push a cleanup for __finally blocks. // Push a cleanup for __finally blocks.
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, &FI); llvm::Function *FinallyFunc =
HelperCGF.GenerateSEHFinallyFunction(*this, *Finally);
EHStack.pushCleanup<PerformSEHFinally>(NormalAndEHCleanup, FinallyFunc);
return; return;
} }
// Otherwise, we must have an __except block. // Otherwise, we must have an __except block.
SEHExceptStmt *Except = S.getExceptHandler(); const SEHExceptStmt *Except = S.getExceptHandler();
assert(Except); assert(Except);
EHCatchScope *CatchScope = EHStack.pushCatch(1); EHCatchScope *CatchScope = EHStack.pushCatch(1);
@@ -1583,40 +1631,17 @@ void CodeGenFunction::EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) {
// In general, we have to emit an outlined filter function. Use the function // In general, we have to emit an outlined filter function. Use the function
// in place of the RTTI typeinfo global that C++ EH uses. // in place of the RTTI typeinfo global that C++ EH uses.
CodeGenFunction FilterCGF(CGM, /*suppressNewContext=*/true);
llvm::Function *FilterFunc = llvm::Function *FilterFunc =
FilterCGF.GenerateSEHFilterFunction(*this, *Except); HelperCGF.GenerateSEHFilterFunction(*this, *Except);
llvm::Constant *OpaqueFunc = llvm::Constant *OpaqueFunc =
llvm::ConstantExpr::getBitCast(FilterFunc, Int8PtrTy); llvm::ConstantExpr::getBitCast(FilterFunc, Int8PtrTy);
CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except")); CatchScope->setHandler(0, OpaqueFunc, createBasicBlock("__except"));
} }
void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI) { void CodeGenFunction::ExitSEHTryStmt(const SEHTryStmt &S) {
// Just pop the cleanup if it's a __finally block. // Just pop the cleanup if it's a __finally block.
if (const SEHFinallyStmt *Finally = S.getFinallyHandler()) { if (S.getFinallyHandler()) {
PopCleanupBlock(); PopCleanupBlock();
assert(FI.ContBB && "did not emit normal cleanup");
// Emit the code into FinallyBB.
CGBuilderTy::InsertPoint SavedIP = Builder.saveIP();
Builder.SetInsertPoint(FI.FinallyBB);
EmitStmt(Finally->getBlock());
if (HaveInsertPoint()) {
if (FI.ResumeBB) {
llvm::Value *IsEH = Builder.CreateLoad(getAbnormalTerminationSlot(),
"abnormal.termination");
IsEH = Builder.CreateICmpEQ(IsEH, llvm::ConstantInt::get(Int8Ty, 0));
Builder.CreateCondBr(IsEH, FI.ContBB, FI.ResumeBB);
} else {
// There was nothing exceptional in the try body, so we only have normal
// control flow.
Builder.CreateBr(FI.ContBB);
}
}
Builder.restoreIP(SavedIP);
return; return;
} }
@@ -1666,7 +1691,13 @@ void CodeGenFunction::EmitSEHLeaveStmt(const SEHLeaveStmt &S) {
if (HaveInsertPoint()) if (HaveInsertPoint())
EmitStopPoint(&S); EmitStopPoint(&S);
assert(!SEHTryEpilogueStack.empty() && // This must be a __leave from a __finally block, which we warn on and is UB.
"sema should have rejected this __leave"); // Just emit unreachable.
if (!isSEHTryScope()) {
Builder.CreateUnreachable();
Builder.ClearInsertionPoint();
return;
}
EmitBranchThroughCleanup(*SEHTryEpilogueStack.back()); EmitBranchThroughCleanup(*SEHTryEpilogueStack.back());
} }

View File

@@ -1021,6 +1021,12 @@ void CodeGenFunction::EmitReturnOfRValue(RValue RV, QualType Ty) {
/// if the function returns void, or may be missing one if the function returns /// if the function returns void, or may be missing one if the function returns
/// non-void. Fun stuff :). /// non-void. Fun stuff :).
void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) { void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
// Returning from an outlined SEH helper is UB, and we already warn on it.
if (IsOutlinedSEHHelper) {
Builder.CreateUnreachable();
Builder.ClearInsertionPoint();
}
// Emit the result value, even if unused, to evalute the side effects. // Emit the result value, even if unused, to evalute the side effects.
const Expr *RV = S.getRetValue(); const Expr *RV = S.getRetValue();

View File

@@ -41,7 +41,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext)
CurFn(nullptr), CapturedStmtInfo(nullptr), CurFn(nullptr), CapturedStmtInfo(nullptr),
SanOpts(CGM.getLangOpts().Sanitize), IsSanitizerScope(false), SanOpts(CGM.getLangOpts().Sanitize), IsSanitizerScope(false),
CurFuncIsThunk(false), AutoreleaseResult(false), SawAsmBlock(false), CurFuncIsThunk(false), AutoreleaseResult(false), SawAsmBlock(false),
BlockInfo(nullptr), BlockPointer(nullptr), IsOutlinedSEHHelper(false), BlockInfo(nullptr), BlockPointer(nullptr),
LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr), LambdaThisCaptureField(nullptr), NormalCleanupDest(nullptr),
NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr), NextCleanupDestIndex(1), FirstBlockInfo(nullptr), EHResumeBlock(nullptr),
ExceptionSlot(nullptr), EHSelectorSlot(nullptr), ExceptionSlot(nullptr), EHSelectorSlot(nullptr),

View File

@@ -263,6 +263,10 @@ public:
/// potentially set the return value. /// potentially set the return value.
bool SawAsmBlock; bool SawAsmBlock;
/// True if the current function is an outlined SEH helper. This can be a
/// finally block or filter expression.
bool IsOutlinedSEHHelper;
const CodeGen::CGBlockInfo *BlockInfo; const CodeGen::CGBlockInfo *BlockInfo;
llvm::Value *BlockPointer; llvm::Value *BlockPointer;
@@ -351,17 +355,6 @@ public:
void exit(CodeGenFunction &CGF); void exit(CodeGenFunction &CGF);
}; };
/// Cleanups can be emitted for two reasons: normal control leaving a region
/// exceptional control flow leaving a region.
struct SEHFinallyInfo {
SEHFinallyInfo()
: FinallyBB(nullptr), ContBB(nullptr), ResumeBB(nullptr) {}
llvm::BasicBlock *FinallyBB;
llvm::BasicBlock *ContBB;
llvm::BasicBlock *ResumeBB;
};
/// Returns true inside SEH __try blocks. /// Returns true inside SEH __try blocks.
bool isSEHTryScope() const { return !SEHTryEpilogueStack.empty(); } bool isSEHTryScope() const { return !SEHTryEpilogueStack.empty(); }
@@ -1052,10 +1045,6 @@ public:
llvm::Value *getExceptionSlot(); llvm::Value *getExceptionSlot();
llvm::Value *getEHSelectorSlot(); llvm::Value *getEHSelectorSlot();
/// Stack slot that contains whether a __finally block is being executed as an
/// EH cleanup or as a normal cleanup.
llvm::Value *getAbnormalTerminationSlot();
/// Returns the contents of the function's exception object and selector /// Returns the contents of the function's exception object and selector
/// slots. /// slots.
llvm::Value *getExceptionFromSlot(); llvm::Value *getExceptionFromSlot();
@@ -2003,12 +1992,19 @@ public:
void EmitCXXTryStmt(const CXXTryStmt &S); void EmitCXXTryStmt(const CXXTryStmt &S);
void EmitSEHTryStmt(const SEHTryStmt &S); void EmitSEHTryStmt(const SEHTryStmt &S);
void EmitSEHLeaveStmt(const SEHLeaveStmt &S); void EmitSEHLeaveStmt(const SEHLeaveStmt &S);
void EnterSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI); void EnterSEHTryStmt(const SEHTryStmt &S);
void ExitSEHTryStmt(const SEHTryStmt &S, SEHFinallyInfo &FI); void ExitSEHTryStmt(const SEHTryStmt &S);
void startOutlinedSEHHelper(CodeGenFunction &ParentCGF, StringRef Name,
QualType RetTy, FunctionArgList &Args,
const Stmt *OutlinedStmt);
llvm::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF, llvm::Function *GenerateSEHFilterFunction(CodeGenFunction &ParentCGF,
const SEHExceptStmt &Except); const SEHExceptStmt &Except);
llvm::Function *GenerateSEHFinallyFunction(CodeGenFunction &ParentCGF,
const SEHFinallyStmt &Finally);
void EmitSEHExceptionCodeSave(); void EmitSEHExceptionCodeSave();
llvm::Value *EmitSEHExceptionCode(); llvm::Value *EmitSEHExceptionCode();
llvm::Value *EmitSEHExceptionInfo(); llvm::Value *EmitSEHExceptionInfo();

View File

@@ -17,30 +17,20 @@ void basic_finally(void) {
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[invoke_cont]] // CHECK: [[invoke_cont]]
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally:[^ ]*]] // CHECK: call void @"\01?fin$0@0@basic_finally@@"(i1 zeroext false, i8* %[[fp]])
//
// CHECK: [[finally]]
// CHECK: call void @cleanup()
// CHECK: load i8, i8* %[[abnormal]]
// CHECK: icmp eq
// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]]
//
// CHECK: [[finallycont]]
// CHECK-NEXT: ret void // CHECK-NEXT: ret void
// //
// CHECK: [[lpad]] // CHECK: [[lpad]]
// CHECK-NEXT: landingpad // CHECK-NEXT: landingpad
// CHECK-NEXT: cleanup // CHECK-NEXT: cleanup
// CHECK: store i8 1, i8* %[[abnormal]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally]] // CHECK: call void @"\01?fin$0@0@basic_finally@@"(i1 zeroext true, i8* %[[fp]])
//
// CHECK: [[resumecont]]
// CHECK: br label %[[ehresume:[^ ]*]]
//
// CHECK: [[ehresume]]
// CHECK: resume { i8*, i32 } // CHECK: resume { i8*, i32 }
// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: call void @cleanup()
// Mostly check that we don't double emit 'r' which would crash. // Mostly check that we don't double emit 'r' which would crash.
void decl_in_finally(void) { void decl_in_finally(void) {
__try { __try {
@@ -67,10 +57,11 @@ l:
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[invoke_cont]] // CHECK: [[invoke_cont]]
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally:[^ ]*]] // CHECK: call void @"\01?fin$0@0@label_in_finally@@"(i1 zeroext false, i8* %[[fp]])
// // CHECK: ret void
// CHECK: [[finally]]
// CHECK: define internal void @"\01?fin$0@0@label_in_finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: br label %[[l:[^ ]*]] // CHECK: br label %[[l:[^ ]*]]
// //
// CHECK: [[l]] // CHECK: [[l]]
@@ -93,32 +84,22 @@ void use_abnormal_termination(void) {
// CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK: to label %[[invoke_cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[invoke_cont]] // CHECK: [[invoke_cont]]
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally:[^ ]*]] // CHECK: call void @"\01?fin$0@0@use_abnormal_termination@@"(i1 zeroext false, i8* %[[fp]])
// // CHECK: ret void
// CHECK: [[finally]]
// CHECK: load i8, i8* %[[abnormal]]
// CHECK: zext i8 %{{.*}} to i32
// CHECK: store i32 %{{.*}}, i32* @crashed
// CHECK: load i8, i8* %[[abnormal]]
// CHECK: icmp eq
// CHECK: br i1 %{{.*}}, label %[[finallycont:[^ ]*]], label %[[resumecont:[^ ]*]]
//
// CHECK: [[finallycont]]
// CHECK-NEXT: ret void
// //
// CHECK: [[lpad]] // CHECK: [[lpad]]
// CHECK-NEXT: landingpad // CHECK-NEXT: landingpad
// CHECK-NEXT: cleanup // CHECK-NEXT: cleanup
// CHECK: store i8 1, i8* %[[abnormal]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally]] // CHECK: call void @"\01?fin$0@0@use_abnormal_termination@@"(i1 zeroext true, i8* %[[fp]])
//
// CHECK: [[resumecont]]
// CHECK: br label %[[ehresume:[^ ]*]]
//
// CHECK: [[ehresume]]
// CHECK: resume { i8*, i32 } // CHECK: resume { i8*, i32 }
// CHECK: define internal void @"\01?fin$0@0@use_abnormal_termination@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: %[[abnormal_zext:[^ ]*]] = zext i1 %abnormal_termination to i32
// CHECK: store i32 %[[abnormal_zext]], i32* @crashed
// CHECK-NEXT: ret void
void noreturn_noop_finally() { void noreturn_noop_finally() {
__try { __try {
__noop(); __noop();
@@ -128,12 +109,13 @@ void noreturn_noop_finally() {
} }
// CHECK-LABEL: define void @noreturn_noop_finally() // CHECK-LABEL: define void @noreturn_noop_finally()
// CHECK: store i8 0, i8* % // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally:[^ ]*]] // CHECK: call void @"\01?fin$0@0@noreturn_noop_finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: [[finally]] // CHECK: ret void
// CHECK: define internal void @"\01?fin$0@0@noreturn_noop_finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: call void @abort() // CHECK: call void @abort()
// CHECK-NEXT: unreachable // CHECK: unreachable
// CHECK-NOT: load
void noreturn_finally() { void noreturn_finally() {
__try { __try {
@@ -148,18 +130,20 @@ void noreturn_finally() {
// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[cont]] // CHECK: [[cont]]
// CHECK: store i8 0, i8* % // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally:[^ ]*]] // CHECK: call void @"\01?fin$0@0@noreturn_finally@@"(i1 zeroext false, i8* %[[fp]])
// // CHECK: ret void
// CHECK: [[finally]]
// CHECK: call void @abort()
// CHECK-NEXT: unreachable
// //
// CHECK: [[lpad]] // CHECK: [[lpad]]
// CHECK: landingpad // CHECK: landingpad
// CHECK-NEXT: cleanup // CHECK-NEXT: cleanup
// CHECK: store i8 1, i8* % // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally]] // CHECK: call void @"\01?fin$0@0@noreturn_finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK: resume { i8*, i32 }
// CHECK: define internal void @"\01?fin$0@0@noreturn_finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: call void @abort()
// CHECK: unreachable
int finally_with_return() { int finally_with_return() {
__try { __try {
@@ -168,15 +152,17 @@ int finally_with_return() {
} }
} }
// CHECK-LABEL: define i32 @finally_with_return() // CHECK-LABEL: define i32 @finally_with_return()
// CHECK: store i8 0, i8* % // CHECK: alloca i32
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: store i32
// // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: [[finally]] // CHECK-NEXT: call void @"\01?fin$0@0@finally_with_return@@"(i1 zeroext false, i8* %[[fp]])
// CHECK-NEXT: br label %[[finallycont:[^ ]*]]
//
// CHECK: [[finallycont]]
// CHECK-NEXT: ret i32 42 // CHECK-NEXT: ret i32 42
// CHECK: define internal void @"\01?fin$0@0@finally_with_return@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK-NOT: br i1
// CHECK-NOT: br label
// CHECK: ret void
int nested___finally___finally() { int nested___finally___finally() {
__try { __try {
__try { __try {
@@ -188,38 +174,28 @@ int nested___finally___finally() {
} }
return 0; return 0;
} }
// CHECK-LABEL: define i32 @nested___finally___finally // CHECK-LABEL: define i32 @nested___finally___finally
// CHECK: store i8 0, i8* % // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[finally]] // CHECK: [[outercont]]
// CHECK-NEXT: store i32 1, i32* % // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: store i32 1, i32* % // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK-NEXT: br label %[[cleanup:[^ ]*]] // CHECK-NEXT: ret i32 0
// //
// The finally's unreachable continuation block: // CHECK: [[lpad]]
// CHECK: store i32 0, i32* % // CHECK-NEXT: landingpad
// CHECK-NEXT: br label %[[cleanup]] // CHECK-NEXT: cleanup
// // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: [[cleanup]] // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK-NEXT: store i8 0, i8* %
// CHECK-NEXT: br label %[[outerfinally:[^ ]*]] // CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// // CHECK: ret void
// CHECK: [[outerfinally]]
// CHECK-NEXT: br label %[[finallycont:[^ ]*]] // CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// // CHECK: unreachable
// CHECK: [[finallycont]]
// CHECK-NEXT: %[[dest:[^ ]*]] = load i32, i32* %
// CHECK-NEXT: switch i32 %[[dest]]
// CHECK-NEXT: i32 0, label %[[cleanupcont:[^ ]*]]
//
// CHECK: [[cleanupcont]]
// CHECK-NEXT: store i32 0, i32* %
// CHECK-NEXT: br label %[[return:[^ ]*]]
//
// CHECK: [[return]]
// CHECK-NEXT: %[[reg:[^ ]*]] = load i32, i32* %
// CHECK-NEXT: ret i32 %[[reg]]
int nested___finally___finally_with_eh_edge() { int nested___finally___finally_with_eh_edge() {
__try { __try {
@@ -234,58 +210,35 @@ int nested___finally___finally_with_eh_edge() {
return 912; return 912;
} }
// CHECK-LABEL: define i32 @nested___finally___finally_with_eh_edge // CHECK-LABEL: define i32 @nested___finally___finally_with_eh_edge
// CHECK: invoke void @might_crash() #3 // CHECK: invoke void @might_crash()
// CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK-NEXT: to label %[[invokecont:[^ ]*]] unwind label %[[lpad1:[^ ]*]]
// //
// CHECK: [[invokecont]] // [[invokecont]]
// CHECK-NEXT: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad2:[^ ]*]]
// CHECK: [[finally]]
// CHECK-NEXT: store i32 899, i32* %
// CHECK-NEXT: store i32 1, i32* %
// CHECK-NEXT: br label %[[cleanup:[^ ]*]]
// //
// The inner finally's unreachable continuation block: // CHECK: [[outercont]]
// CHECK: store i32 0, i32* % // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[cleanup]] // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext false, i8* %[[fp]])
// CHECK-NEXT: ret i32 912
// //
// CHECK: [[cleanup]] // CHECK: [[lpad1]]
// CHECK-NEXT: store i8 0, i8* %
// CHECK-NEXT: br label %[[outerfinally:[^ ]*]]
//
// CHECK: [[outerfinally]]
// CHECK-NEXT: %[[abnormallocal:[^ ]*]] = load i8, i8* %[[abnormal]]
// CHECK-NEXT: %[[reg:[^ ]*]] = icmp eq i8 %[[abnormallocal]], 0
// CHECK-NEXT: br i1 %[[reg]], label %[[finallycont:[^ ]*]], label %[[finallyresume:[^ ]*]]
//
// CHECK: [[finallycont]]
// CHECK-NEXT: %[[dest:[^ ]*]] = load i32, i32* %
// CHECK-NEXT: switch i32 %[[dest]]
// CHECK-NEXT: i32 0, label %[[cleanupcont:[^ ]*]]
//
// CHECK: [[cleanupcont]]
// CHECK-NEXT: store i32 912, i32* %
// CHECK-NEXT: br label %[[return:[^ ]*]]
//
//
// CHECK: [[lpad]]
// CHECK-NEXT: landingpad // CHECK-NEXT: landingpad
// CHECK-NEXT: cleanup // CHECK-NEXT: cleanup
// CHECK: store i8 1, i8* %[[abnormal]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: br label %[[finally]] // CHECK: invoke void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext true, i8* %[[fp]])
// CHECK: to label %[[outercont:[^ ]*]] unwind label %[[lpad2]]
// //
// The inner finally's unreachable resume block: // CHECK: [[lpad2]]
// CHECK: store i8 1, i8* %[[abnormal]] // CHECK-NEXT: landingpad
// CHECK-NEXT: br label %[[outerfinally]] // CHECK-NEXT: cleanup
// // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK: [[finallyresume]] // CHECK: call void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext true, i8* %[[fp]])
// CHECK-NEXT: br label %[[ehresume:[^ ]*]]
//
// CHECK: [[return]]
// CHECK-NEXT: %[[reg:[^ ]*]] = load i32, i32* %
// CHECK-NEXT: ret i32 %[[reg]]
//
// The ehresume block, not reachable either.
// CHECK: [[ehresume]]
// CHECK: resume // CHECK: resume
// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: ret void
// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally_with_eh_edge@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: unreachable

View File

@@ -1,6 +1,6 @@
// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
void g(); void g(void);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// __leave with __except // __leave with __except
@@ -38,7 +38,7 @@ int __leave_with___except() {
return 1; return 1;
} }
// CHECK-LABEL: define i32 @__leave_with___except() // CHECK-LABEL: define i32 @__leave_with___except()
// CHECK: invoke void bitcast (void (...)* @g to void ()*)() // CHECK: invoke void @g()
// CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}} // CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}}
// For __excepts, instead of an explicit __try.__leave label, we could use // For __excepts, instead of an explicit __try.__leave label, we could use
// use invoke.cont as __leave jump target instead. However, not doing this // use invoke.cont as __leave jump target instead. However, not doing this
@@ -74,8 +74,8 @@ int __leave_with___finally_simple() {
// CHECK-NEXT: br label %[[tryleave:[^ ]*]] // CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23 // CHECK-NOT: store i32 23
// CHECK: [[tryleave]] // CHECK: [[tryleave]]
// CHECK-NEXT: store i8 0, i8* % // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label % // CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally_simple@@"(i1 zeroext false, i8* %[[fp]])
// __finally block doesn't return, __finally.cont doesn't exist. // __finally block doesn't return, __finally.cont doesn't exist.
int __leave_with___finally_noreturn() { int __leave_with___finally_noreturn() {
@@ -94,8 +94,8 @@ int __leave_with___finally_noreturn() {
// CHECK-NEXT: br label %[[tryleave:[^ ]*]] // CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23 // CHECK-NOT: store i32 23
// CHECK: [[tryleave]] // CHECK: [[tryleave]]
// CHECK-NEXT: store i8 0, i8* % // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label % // CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally_noreturn@@"(i1 zeroext false, i8* %[[fp]])
// The "normal" case. // The "normal" case.
int __leave_with___finally() { int __leave_with___finally() {
@@ -110,7 +110,7 @@ int __leave_with___finally() {
return 1; return 1;
} }
// CHECK-LABEL: define i32 @__leave_with___finally() // CHECK-LABEL: define i32 @__leave_with___finally()
// CHECK: invoke void bitcast (void (...)* @g to void ()*)() // CHECK: invoke void @g()
// CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}} // CHECK-NEXT: to label %[[cont:.*]] unwind label %{{.*}}
// For __finally, there needs to be an explicit __try.__leave, because // For __finally, there needs to be an explicit __try.__leave, because
// abnormal.termination.slot needs to be set there. // abnormal.termination.slot needs to be set there.
@@ -118,8 +118,8 @@ int __leave_with___finally() {
// CHECK-NEXT: br label %[[tryleave:[^ ]*]] // CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23 // CHECK-NOT: store i32 23
// CHECK: [[tryleave]] // CHECK: [[tryleave]]
// CHECK-NEXT: store i8 0, i8* % // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label % // CHECK-NEXT: call void @"\01?fin$0@0@__leave_with___finally@@"(i1 zeroext false, i8* %[[fp]])
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@@ -142,45 +142,37 @@ int nested___except___finally() {
} }
return 1; return 1;
} }
// The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define i32 @nested___except___finally() // CHECK-LABEL: define i32 @nested___except___finally()
// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)() // CHECK-LABEL: invoke void @g()
// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] // CHECK-NEXT: to label %[[g1_cont1:.*]] unwind label %[[g1_lpad:.*]]
// CHECK: [[g1_cont]] // CHECK: [[g1_cont1]]
// CHECK-NEXT: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK-NEXT: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: invoke void @"\01?fin$0@0@nested___except___finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK-NEXT: to label %[[fin_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[finally]] // CHECK: [[fin_cont]]
// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)()
// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[g2_cont]]
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// Unused __finally continuation block
// CHECK: store i32 51, i32* % // CHECK: store i32 51, i32* %
// CHECK-NEXT: br label %[[tryleave]]
// CHECK: [[tryleave]]
// CHECK-NEXT: br label %[[trycont:[^ ]*]] // CHECK-NEXT: br label %[[trycont:[^ ]*]]
// CHECK: [[g1_lpad]] // CHECK: [[g1_lpad]]
// CHECK: store i8 1, i8* % // CHECK-NEXT: landingpad
// CHECK-NEXT: br label %[[finally]] // CHECK-NEXT: catch i8* null
// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: invoke void @"\01?fin$0@0@nested___except___finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK-NEXT: to label %[[g1_resume:.*]] unwind label %[[g2_lpad]]
// CHECK: [[g2_lpad]] // CHECK: [[g2_lpad]]
// CHECK-NOT: %[[abnormal]] // CHECK: br label %[[trycont]]
// CHECK: br label %[[except:[^ ]*]]
// CHECK: [[except]]
// CHECK-NEXT: br label %[[trycont]]
// CHECK: [[trycont]] // CHECK: [[trycont]]
// CHECK-NEXT: ret i32 1 // CHECK-NEXT: ret i32 1
// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___except___finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: call void @g()
// CHECK: unreachable
int nested___except___except() { int nested___except___except() {
int myres = 0; int myres = 0;
__try { __try {
@@ -202,7 +194,7 @@ int nested___except___except() {
// The order of basic blocks in the below doesn't matter. // The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define i32 @nested___except___except() // CHECK-LABEL: define i32 @nested___except___except()
// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)() // CHECK-LABEL: invoke void @g()
// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] // CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]
// CHECK: [[g1_cont]] // CHECK: [[g1_cont]]
@@ -213,7 +205,7 @@ int nested___except___except() {
// CHECK: br label %[[except:[^ ]*]] // CHECK: br label %[[except:[^ ]*]]
// CHECK: [[except]] // CHECK: [[except]]
// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)() #3 // CHECK-NEXT: invoke void @g()
// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] // CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[g2_cont]] // CHECK: [[g2_cont]]
@@ -256,7 +248,7 @@ int nested___finally___except() {
// The order of basic blocks in the below doesn't matter. // The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define i32 @nested___finally___except() // CHECK-LABEL: define i32 @nested___finally___except()
// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)() // CHECK-LABEL: invoke void @g()
// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] // CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]
// CHECK: [[g1_cont]] // CHECK: [[g1_cont]]
@@ -266,7 +258,7 @@ int nested___finally___except() {
// CHECK: br label %[[except:[^ ]*]] // CHECK: br label %[[except:[^ ]*]]
// CHECK: [[except]] // CHECK: [[except]]
// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)() // CHECK-NEXT: invoke void @g()
// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]] // CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[g2_cont]] // CHECK: [[g2_cont]]
@@ -274,31 +266,25 @@ int nested___finally___except() {
// CHECK-NOT: 23 // CHECK-NOT: 23
// CHECK: [[g2_lpad]] // CHECK: [[g2_lpad]]
// CHECK: store i8 1, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___except@@"(i1 zeroext true, i8* %[[fp]])
// CHECK-NEXT: br label %[[ehresume:[^ ]*]]
// CHECK: [[trycont]] // CHECK: [[trycont]]
// CHECK: store i32 51, i32* % // CHECK: store i32 51, i32* %
// CHECK-NEXT: br label %[[tryleave]] // CHECK-NEXT: br label %[[tryleave]]
// CHECK: [[tryleave]] // CHECK: [[tryleave]]
// CHECK-NEXT: store i8 0, i8* %[[abnormal]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___except@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: [[finally]]
// CHECK-NEXT: %[[abnormallocal:[^ ]*]] = load i8, i8* %[[abnormal]]
// CHECK-NEXT: %[[reg:[^ ]*]] = icmp eq i8 %[[abnormallocal]], 0
// CHECK-NEXT: br i1 %[[reg]], label %[[finallycont:[^ ]*]], label %[[finallyresume:[^ ]*]]
// CHECK: [[finallycont]]
// CHECK-NEXT: ret i32 1 // CHECK-NEXT: ret i32 1
// CHECK: [[finallyresume]]
// CHECK-NEXT: br label %[[ehresume:[^ ]*]]
// CHECK: [[ehresume]] // CHECK: [[ehresume]]
// CHECK: resume // CHECK: resume
// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___except@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: ret void
int nested___finally___finally() { int nested___finally___finally() {
int myres = 0; int myres = 0;
__try { __try {
@@ -320,51 +306,44 @@ int nested___finally___finally() {
// The order of basic blocks in the below doesn't matter. // The order of basic blocks in the below doesn't matter.
// CHECK-LABEL: define i32 @nested___finally___finally() // CHECK-LABEL: define i32 @nested___finally___finally()
// CHECK-LABEL: invoke void bitcast (void (...)* @g to void ()*)() // CHECK-LABEL: invoke void @g()
// CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]] // CHECK-NEXT: to label %[[g1_cont:.*]] unwind label %[[g1_lpad:.*]]
// CHECK: [[g1_cont]] // CHECK: [[g1_cont]]
// CHECK: store i32 16, i32* %[[myres:[^ ]*]], // CHECK: store i32 16, i32* %[[myres:[^ ]*]],
// CHECK: store i8 0, i8* %[[abnormal:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK-NEXT: to label %[[finally_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[finally]] // CHECK: [[finally_cont]]
// CHECK-NEXT: invoke void bitcast (void (...)* @g to void ()*)() #3
// CHECK-NEXT: to label %[[g2_cont:.*]] unwind label %[[g2_lpad:.*]]
// CHECK: [[g2_cont]]
// CHECK-NEXT: br label %[[tryleave:[^ ]*]]
// CHECK-NOT: store i32 23
// There's an unreachable block for the skipped `myres = 51`.
// CHECK: store i32 51, i32* %[[myres]] // CHECK: store i32 51, i32* %[[myres]]
// CHECK-NEXT: br label %[[tryleave]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: [[tryleave]]
// CHECK-NEXT: store i8 0, i8* %[[abnormal]]
// CHECK-NEXT: br label %[[outerfinally:[^ ]*]]
// CHECK: [[outerfinally]]
// CHECK-NEXT: %[[abnormallocal:[^ ]*]] = load i8, i8* %[[abnormal]]
// CHECK-NEXT: %[[reg:[^ ]*]] = icmp eq i8 %[[abnormallocal]], 0
// CHECK-NEXT: br i1 %[[reg]], label %[[finallycont:[^ ]*]], label %[[finallyresume:[^ ]*]]
// CHECK: [[finallycont]]
// CHECK-NEXT: ret i32 1 // CHECK-NEXT: ret i32 1
// CHECK: [[g1_lpad]] // CHECK: [[g1_lpad]]
// CHECK: store i8 1, i8* %[[abnormal]] // CHECK-NEXT: landingpad
// CHECK-NEXT: br label %[[finally:[^ ]*]] // CHECK-NEXT: cleanup
// CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: invoke void @"\01?fin$1@0@nested___finally___finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK-NEXT: to label %[[finally_cont2:.*]] unwind label %[[g2_lpad]]
// CHECK: [[g2_lpad]] // CHECK: [[g2_lpad]]
// CHECK: br label %[[ehcleanup:[^ ]*]] // CHECK-NEXT: landingpad
// CHECK-NEXT: cleanup
// CHECK: br label %[[ehcleanup:.*]]
// CHECK: [[finally_cont2]]
// CHECK: br label %[[ehcleanup]]
// CHECK: [[ehcleanup]] // CHECK: [[ehcleanup]]
// CHECK-NEXT: store i8 1, i8* %[[abnormal]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// CHECK-NEXT: br label %[[outerfinally]] // CHECK-NEXT: call void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK: [[finallyresume]]
// CHECK-NEXT: br label %[[ehresume:[^ ]*]]
// CHECK: [[ehresume]]
// CHECK: resume // CHECK: resume
// CHECK-LABEL: define internal void @"\01?fin$0@0@nested___finally___finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: ret void
// CHECK-LABEL: define internal void @"\01?fin$1@0@nested___finally___finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: call void @g()
// CHECK: unreachable

View File

@@ -118,8 +118,6 @@ int nested_try(void) {
// CHECK: [[inner_try_cont]] // CHECK: [[inner_try_cont]]
// CHECK: br label %[[outer_try_cont]] // CHECK: br label %[[outer_try_cont]]
// FIXME: This lowering of __finally can't actually work, it will have to
// change.
static unsigned g = 0; static unsigned g = 0;
void basic_finally(void) { void basic_finally(void) {
++g; ++g;
@@ -138,24 +136,23 @@ void basic_finally(void) {
// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]] // CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
// //
// CHECK: [[cont]] // CHECK: [[cont]]
// CHECK: br label %[[finally:[^ ]*]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// // CHECK: call void @"\01?fin$0@0@basic_finally@@"(i1 zeroext false, i8* %[[fp]])
// CHECK: [[finally]]
// CHECK: load i32, i32* @g
// CHECK: add i32 %{{.*}}, -1
// CHECK: store i32 %{{.*}}, i32* @g
// CHECK: icmp eq
// CHECK: br i1 %{{.*}}, label
//
// CHECK: ret void // CHECK: ret void
// //
// CHECK: [[lpad]] // CHECK: [[lpad]]
// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) // CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// CHECK-NEXT: cleanup // CHECK-NEXT: cleanup
// CHECK: br label %[[finally]] // CHECK: %[[fp:[^ ]*]] = call i8* @llvm.frameaddress(i32 0)
// // CHECK: call void @"\01?fin$0@0@basic_finally@@"(i1 zeroext true, i8* %[[fp]])
// CHECK: resume // CHECK: resume
// CHECK: define internal void @"\01?fin$0@0@basic_finally@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
// CHECK: load i32, i32* @g, align 4
// CHECK: add i32 %{{.*}}, -1
// CHECK: store i32 %{{.*}}, i32* @g, align 4
// CHECK: ret void
int returns_int(void); int returns_int(void);
int except_return(void) { int except_return(void) {
__try { __try {