aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/ExceptionHandler.cc
blob: 048f8a67603716de9fa85fafc0384ea471c88719 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

#include "cps/ExceptionHandler.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Instructions.h"
#include <cassert>

using namespace llvm;
using namespace cps;

void ExceptionHandler::ensureRuntime() {
    if (IntTypeInfo) return;
    auto *Ptr = PointerType::getUnqual(Context);
    auto *Void = Type::getVoidTy(Context);
    Allocate = Module.getOrInsertFunction("__cxa_allocate_exception",
        FunctionType::get(Ptr, {Type::getInt64Ty(Context)}, false));
    Throw = Module.getOrInsertFunction("__cxa_throw",
        FunctionType::get(Void, {Ptr, Ptr, Ptr}, false));
    BeginCatch = Module.getOrInsertFunction("__cxa_begin_catch",
        FunctionType::get(Ptr, {Ptr}, false));
    EndCatch = Module.getOrInsertFunction("__cxa_end_catch",
        FunctionType::get(Void, false));
#if defined(__SEH__)
    const char *PersonalityName = "__gxx_personality_seh0";
#elif defined(__USING_SJLJ_EXCEPTIONS__)
    const char *PersonalityName = "__gxx_personality_sj0";
#else
    const char *PersonalityName = "__gxx_personality_v0";
#endif
    Personality = Module.getOrInsertFunction(PersonalityName,
        FunctionType::get(Type::getInt32Ty(Context), true));
    IntTypeInfo = new GlobalVariable(Module, Ptr, true, GlobalValue::ExternalLinkage,
                                      nullptr, "_ZTIi");
    cast<Function>(Allocate.getCallee())->setDoesNotThrow();
    cast<Function>(BeginCatch.getCallee())->setDoesNotThrow();
    // Only anonymous integer exceptions are thrown; there is no throwing
    // exception-object destructor to invoke when a catch ends.
    cast<Function>(EndCatch.getCallee())->setDoesNotThrow();
    cast<Function>(Throw.getCallee())->setDoesNotReturn();
}

void ExceptionHandler::prepareFunction(Function *F) {
    F->setUWTableKind(UWTableKind::Default);
}

void ExceptionHandler::pushHandler(BasicBlock *Target) {
    auto *F = Builder.GetInsertBlock()->getParent();
    IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin());
    auto *Exception = Entry.CreateAlloca(PointerType::getUnqual(Context), nullptr,
                                         "caught_exception");
    auto &State = States[F];
    State.Handlers.push_back({Target, Exception, State.CatchDepth});
}

AllocaInst *ExceptionHandler::popHandler() {
    auto &Handlers = States[Builder.GetInsertBlock()->getParent()].Handlers;
    assert(!Handlers.empty());
    auto *Exception = Handlers.back().Exception;
    Handlers.pop_back();
    return Exception;
}

void ExceptionHandler::beginCatch(AllocaInst *Exception) {
    ensureRuntime();
    auto *Ptr = Builder.CreateLoad(PointerType::getUnqual(Context), Exception);
    Builder.CreateCall(BeginCatch, {Ptr});
    ++States[Builder.GetInsertBlock()->getParent()].CatchDepth;
}

void ExceptionHandler::endCatch() {
    auto &Depth = States[Builder.GetInsertBlock()->getParent()].CatchDepth;
    assert(Depth != 0);
    --Depth;
    if (!Builder.GetInsertBlock()->getTerminator()) Builder.CreateCall(EndCatch);
}

void ExceptionHandler::emitReturnCleanup() {
    auto &State = States[Builder.GetInsertBlock()->getParent()];
    for (unsigned I = 0; I < State.CatchDepth; ++I) Builder.CreateCall(EndCatch);
}

BasicBlock *ExceptionHandler::emitUnwindDestination() {
    auto *F = Builder.GetInsertBlock()->getParent();
    auto &State = States[F];
    if (State.Handlers.empty() && State.CatchDepth == 0) return nullptr;
    ensureRuntime();
    F->setPersonalityFn(cast<Constant>(Personality.getCallee()));
    auto Saved = Builder.saveIP();
    auto *Unwind = BasicBlock::Create(Context, "exception_unwind", F);
    Builder.SetInsertPoint(Unwind);
    auto *LPType = StructType::get(PointerType::getUnqual(Context), Type::getInt32Ty(Context));
    auto *LP = Builder.CreateLandingPad(LPType, 1, "exception");
    const Handler *Handler = State.Handlers.empty() ? nullptr : &State.Handlers.back();
    unsigned TargetDepth = Handler ? Handler->CatchDepth : 0;
    if (Handler) LP->addClause(ConstantPointerNull::get(PointerType::getUnqual(Context)));
    if (State.CatchDepth > TargetDepth) LP->setCleanup(true);
    // Finish catches we are leaving before entering the outer handler (or
    // resuming propagation through a function with no local handler).
    for (unsigned I = TargetDepth; I < State.CatchDepth; ++I) Builder.CreateCall(EndCatch);
    if (Handler) {
        Builder.CreateStore(Builder.CreateExtractValue(LP, 0), Handler->Exception);
        Builder.CreateBr(Handler->Target);
    } else {
        Builder.CreateResume(LP);
    }
    Builder.restoreIP(Saved);
    return Unwind;
}

Value *ExceptionHandler::emitCall(FunctionCallee Callee, ArrayRef<Value *> Args,
                                   const Twine &Name) {
    auto *Unwind = emitUnwindDestination();
    if (!Unwind) return Builder.CreateCall(Callee, Args, Name);
    auto *Cont = BasicBlock::Create(Context, "invoke_cont", Builder.GetInsertBlock()->getParent());
    auto *Call = Builder.CreateInvoke(Callee, Cont, Unwind, Args, Name);
    Builder.SetInsertPoint(Cont);
    return Call;
}

void ExceptionHandler::emitThrow() {
    ensureRuntime();
    // sizeof(int) is a target-folded null GEP, as for every other allocation.
    auto *I32 = Type::getInt32Ty(Context);
    auto *Null = ConstantPointerNull::get(PointerType::getUnqual(Context));
    auto *Size = ConstantExpr::getSizeOf(I32);
    auto *Object = Builder.CreateCall(Allocate, {Size}, "anonymous_exception");
    Builder.CreateStore(ConstantInt::get(I32, 0), Object);
    emitCall(Throw, {Object, IntTypeInfo, Null});
    Builder.CreateUnreachable();
}