[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:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user