forked from facebook/hermes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExceptions.cpp
More file actions
175 lines (159 loc) · 6 KB
/
Exceptions.cpp
File metadata and controls
175 lines (159 loc) · 6 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#define DEBUG_TYPE "exceptions"
#include "hermes/BCGen/Exceptions.h"
#include "hermes/IR/CFG.h"
#include "hermes/IR/IR.h"
#include "hermes/IR/IRBuilder.h"
#include "llvh/Support/Debug.h"
#include "llvh/Support/raw_ostream.h"
using namespace hermes;
using llvh::isa;
/// Construct the list of basic blocks covered by each catch instruction.
/// Use recursion to handle nested catches.
bool hermes::constructCatchMap(
Function *F,
CatchInfoMap &catchInfoMap,
llvh::SmallVectorImpl<CatchInst *> &aliveCatches,
llvh::SmallPtrSetImpl<BasicBlock *> &visited,
BasicBlock *currentBlock,
uint32_t maxRecursionDepth) {
if (maxRecursionDepth == 0) {
F->getContext().getSourceErrorManager().error(
F->getSourceRange(), "Too deeply nested try/catch");
return false;
}
if (!visited.insert(currentBlock).second)
return true;
// TryEndInst can only show up at the beginning of a block;
// TryStartInst can only show up at the end of a block.
// Hence we process the block with the order:
// TryEndInst => block body => TryStartInst => successors.
bool isTryStartBlock = llvh::isa<TryStartInst>(currentBlock->getTerminator());
bool isTryEndBlock = llvh::isa<TryEndInst>(¤tBlock->front());
CatchInst *currentCatch = nullptr;
if (isTryEndBlock) {
// Hit a TryEndInst, marking the end of a try region, save the current
// catch.
currentCatch = aliveCatches.pop_back_val();
}
// For every nested try that's alive, we add the basic block into it.
for (auto &aliveCatche : aliveCatches) {
catchInfoMap[aliveCatche].coveredBlockList.push_back(currentBlock);
}
if (isTryStartBlock) {
// Hit a TryStartInst, marking the start of a new try region.
// The first instruction of the catch target block must be CatchInst.
auto tryStartInst = cast<TryStartInst>(currentBlock->getTerminator());
auto catchInst = cast<CatchInst>(&tryStartInst->getCatchTarget()->front());
catchInfoMap[catchInst].depth = aliveCatches.size();
// Pushing the CatchInst to the try stack, and continue scan the try body.
aliveCatches.push_back(catchInst);
if (!constructCatchMap(
F,
catchInfoMap,
aliveCatches,
visited,
tryStartInst->getTryBody(),
maxRecursionDepth - 1))
return false;
aliveCatches.pop_back();
// We also want to continue scan into the catch blocks.
if (!constructCatchMap(
F,
catchInfoMap,
aliveCatches,
visited,
tryStartInst->getCatchTarget(),
maxRecursionDepth - 1))
return false;
} else {
// No TryStartInst, we iterate successors normally.
for (auto itr = succ_begin(currentBlock), e = succ_end(currentBlock);
itr != e;
++itr) {
if (!constructCatchMap(
F,
catchInfoMap,
aliveCatches,
visited,
*itr,
maxRecursionDepth - 1))
return false;
}
}
if (isTryEndBlock) {
// After coming back from the recursion, we recover the stack.
assert(currentCatch && "currentCatch is null when there is TryEndInst");
aliveCatches.push_back(currentCatch);
}
return true;
}
ExceptionEntryList hermes::generateExceptionHandlers(
CatchInfoMap &catchInfoMap,
BasicBlockInfoMap &bbMap,
Function *F) {
// Construct the list of blocks and depth covered by each CatchInst.
llvh::SmallVector<CatchInst *, 4> aliveCatches{};
llvh::SmallPtrSet<BasicBlock *, 32> visited{};
static constexpr uint32_t MAX_RECURSION_DEPTH = 1024;
if (!constructCatchMap(
F,
catchInfoMap,
aliveCatches,
visited,
&F->front(),
MAX_RECURSION_DEPTH))
return {};
ExceptionEntryList exception_entries;
for (auto I : catchInfoMap) {
auto &catchInfo = I.second;
// The basic blocks covered by a catch instruction may not be continuous.
// For each basic block, we walk through the current list of ranges,
// and try to merge them into a minimum number of ranges.
llvh::SmallVector<std::pair<uint32_t, uint32_t>, 4> catch_ranges;
for (auto BB : catchInfo.coveredBlockList) {
auto it = bbMap.find(BB);
assert(it != bbMap.end() && "Basic Block missing.");
auto resolved_loc = it->second;
if (resolved_loc.first == resolved_loc.second) {
// Empty basic block, skip.
continue;
}
catch_ranges.push_back(resolved_loc);
}
std::sort(catch_ranges.begin(), catch_ranges.end());
// After ranges are sorted, a range could only be merged with it's
// previous range, if they are adjacent.
// Note: no range can overlap, as basic blocks do not overlap.
int nextIndex = 0;
for (auto resolved_loc : catch_ranges) {
// If we are looking at the first range, or the range cannot
// be merged with the previous range, we store this range.
if (nextIndex == 0 ||
catch_ranges[nextIndex - 1].second != resolved_loc.first) {
catch_ranges[nextIndex++] = resolved_loc;
continue;
}
// Otherwise we merge with the previous range.
catch_ranges[nextIndex - 1].second = resolved_loc.second;
}
// The merging happened in-place. Hence we need to throw away the rest.
catch_ranges.resize(nextIndex);
// For each range, we register it as an exception handler entry.
for (auto range : catch_ranges) {
exception_entries.push_back({(uint32_t)range.first,
(uint32_t)range.second,
(uint32_t)catchInfo.catchLocation,
catchInfo.depth});
}
}
// Sort ranges by depth. In hermes, depth increase when you nest try inside
// try/catch/finally.
std::sort(exception_entries.begin(), exception_entries.end());
return exception_entries;
}