Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp
Line
Count
Source (jump to first uncovered line)
1
//=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
///
9
/// \file
10
/// \brief Does various transformations for exception handling.
11
///
12
//===----------------------------------------------------------------------===//
13
14
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
15
#include "WebAssembly.h"
16
#include "WebAssemblySubtarget.h"
17
#include "WebAssemblyUtilities.h"
18
#include "llvm/ADT/SmallSet.h"
19
#include "llvm/CodeGen/MachineInstrBuilder.h"
20
#include "llvm/CodeGen/WasmEHFuncInfo.h"
21
#include "llvm/MC/MCAsmInfo.h"
22
using namespace llvm;
23
24
#define DEBUG_TYPE "wasm-late-eh-prepare"
25
26
namespace {
27
class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
28
4.88k
  StringRef getPassName() const override {
29
4.88k
    return "WebAssembly Late Prepare Exception";
30
4.88k
  }
31
32
  bool runOnMachineFunction(MachineFunction &MF) override;
33
  bool addCatches(MachineFunction &MF);
34
  bool replaceFuncletReturns(MachineFunction &MF);
35
  bool removeUnnecessaryUnreachables(MachineFunction &MF);
36
  bool addExceptionExtraction(MachineFunction &MF);
37
  bool restoreStackPointer(MachineFunction &MF);
38
39
public:
40
  static char ID; // Pass identification, replacement for typeid
41
427
  WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
42
};
43
} // end anonymous namespace
44
45
char WebAssemblyLateEHPrepare::ID = 0;
46
INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
47
                "WebAssembly Late Exception Preparation", false, false)
48
49
426
FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
50
426
  return new WebAssemblyLateEHPrepare();
51
426
}
52
53
// Returns the nearest EH pad that dominates this instruction. This does not use
54
// dominator analysis; it just does BFS on its predecessors until arriving at an
55
// EH pad. This assumes valid EH scopes so the first EH pad it arrives in all
56
// possible search paths should be the same.
57
// Returns nullptr in case it does not find any EH pad in the search, or finds
58
// multiple different EH pads.
59
74
static MachineBasicBlock *getMatchingEHPad(MachineInstr *MI) {
60
74
  MachineFunction *MF = MI->getParent()->getParent();
61
74
  SmallVector<MachineBasicBlock *, 2> WL;
62
74
  SmallPtrSet<MachineBasicBlock *, 2> Visited;
63
74
  WL.push_back(MI->getParent());
64
74
  MachineBasicBlock *EHPad = nullptr;
65
169
  while (!WL.empty()) {
66
95
    MachineBasicBlock *MBB = WL.pop_back_val();
67
95
    if (Visited.count(MBB))
68
0
      continue;
69
95
    Visited.insert(MBB);
70
95
    if (MBB->isEHPad()) {
71
74
      if (EHPad && 
EHPad != MBB0
)
72
0
        return nullptr;
73
74
      EHPad = MBB;
74
74
      continue;
75
74
    }
76
21
    if (MBB == &MF->front())
77
0
      return nullptr;
78
21
    WL.append(MBB->pred_begin(), MBB->pred_end());
79
21
  }
80
74
  return EHPad;
81
74
}
82
83
// Erase the specified BBs if the BB does not have any remaining predecessors,
84
// and also all its dead children.
85
template <typename Container>
86
38
static void eraseDeadBBsAndChildren(const Container &MBBs) {
87
38
  SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
88
47
  while (!WL.empty()) {
89
9
    MachineBasicBlock *MBB = WL.pop_back_val();
90
9
    if (!MBB->pred_empty())
91
6
      continue;
92
3
    SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(),
93
3
                                              MBB->succ_end());
94
3
    WL.append(MBB->succ_begin(), MBB->succ_end());
95
3
    for (auto *Succ : Succs)
96
0
      MBB->removeSuccessor(Succ);
97
3
    MBB->eraseFromParent();
98
3
  }
99
38
}
100
101
4.45k
bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
102
4.45k
  LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"
103
4.45k
                       "********** Function: "
104
4.45k
                    << MF.getName() << '\n');
105
4.45k
106
4.45k
  if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
107
4.45k
      ExceptionHandling::Wasm)
108
4.40k
    return false;
109
53
110
53
  bool Changed = false;
111
53
  if (MF.getFunction().hasPersonalityFn()) {
112
42
    Changed |= addCatches(MF);
113
42
    Changed |= replaceFuncletReturns(MF);
114
42
  }
115
53
  Changed |= removeUnnecessaryUnreachables(MF);
116
53
  if (MF.getFunction().hasPersonalityFn()) {
117
42
    Changed |= addExceptionExtraction(MF);
118
42
    Changed |= restoreStackPointer(MF);
119
42
  }
120
53
  return Changed;
121
53
}
122
123
// Add catch instruction to beginning of catchpads and cleanuppads.
124
42
bool WebAssemblyLateEHPrepare::addCatches(MachineFunction &MF) {
125
42
  bool Changed = false;
126
42
  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
127
42
  MachineRegisterInfo &MRI = MF.getRegInfo();
128
255
  for (auto &MBB : MF) {
129
255
    if (MBB.isEHPad()) {
130
71
      Changed = true;
131
71
      auto InsertPos = MBB.begin();
132
71
      if (InsertPos->isEHLabel()) // EH pad starts with an EH label
133
71
        ++InsertPos;
134
71
      unsigned DstReg = MRI.createVirtualRegister(&WebAssembly::EXNREFRegClass);
135
71
      BuildMI(MBB, InsertPos, MBB.begin()->getDebugLoc(),
136
71
              TII.get(WebAssembly::CATCH), DstReg);
137
71
    }
138
255
  }
139
42
  return Changed;
140
42
}
141
142
42
bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
143
42
  bool Changed = false;
144
42
  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
145
42
146
255
  for (auto &MBB : MF) {
147
255
    auto Pos = MBB.getFirstTerminator();
148
255
    if (Pos == MBB.end())
149
60
      continue;
150
195
    MachineInstr *TI = &*Pos;
151
195
152
195
    switch (TI->getOpcode()) {
153
195
    case WebAssembly::CATCHRET: {
154
59
      // Replace a catchret with a branch
155
59
      MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
156
59
      if (!MBB.isLayoutSuccessor(TBB))
157
49
        BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
158
49
            .addMBB(TBB);
159
59
      TI->eraseFromParent();
160
59
      Changed = true;
161
59
      break;
162
195
    }
163
195
    case WebAssembly::CLEANUPRET:
164
28
    case WebAssembly::RETHROW_IN_CATCH: {
165
28
      // Replace a cleanupret/rethrow_in_catch with a rethrow
166
28
      auto *EHPad = getMatchingEHPad(TI);
167
28
      auto CatchPos = EHPad->begin();
168
28
      if (CatchPos->isEHLabel()) // EH pad starts with an EH label
169
28
        ++CatchPos;
170
28
      MachineInstr *Catch = &*CatchPos;
171
28
      unsigned ExnReg = Catch->getOperand(0).getReg();
172
28
      BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
173
28
          .addReg(ExnReg);
174
28
      TI->eraseFromParent();
175
28
      Changed = true;
176
28
      break;
177
28
    }
178
195
    }
179
195
  }
180
42
  return Changed;
181
42
}
182
183
bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
184
53
    MachineFunction &MF) {
185
53
  bool Changed = false;
186
263
  for (auto &MBB : MF) {
187
1.03k
    for (auto &MI : MBB) {
188
1.03k
      if (MI.getOpcode() != WebAssembly::THROW &&
189
1.03k
          
MI.getOpcode() != WebAssembly::RETHROW1.02k
)
190
998
        continue;
191
38
      Changed = true;
192
38
193
38
      // The instruction after the throw should be an unreachable or a branch to
194
38
      // another BB that should eventually lead to an unreachable. Delete it
195
38
      // because throw itself is a terminator, and also delete successors if
196
38
      // any.
197
38
      MBB.erase(std::next(MI.getIterator()), MBB.end());
198
38
      SmallVector<MachineBasicBlock *, 8> Succs(MBB.succ_begin(),
199
38
                                                MBB.succ_end());
200
38
      for (auto *Succ : Succs)
201
9
        if (!Succ->isEHPad())
202
3
          MBB.removeSuccessor(Succ);
203
38
      eraseDeadBBsAndChildren(Succs);
204
38
    }
205
263
  }
206
53
207
53
  return Changed;
208
53
}
209
210
// Wasm uses 'br_on_exn' instruction to check the tag of an exception. It takes
211
// exnref type object returned by 'catch', and branches to the destination if it
212
// matches a given tag. We currently use __cpp_exception symbol to represent the
213
// tag for all C++ exceptions.
214
//
215
// block $l (result i32)
216
//   ...
217
//   ;; exnref $e is on the stack at this point
218
//   br_on_exn $l $e ;; branch to $l with $e's arguments
219
//   ...
220
// end
221
// ;; Here we expect the extracted values are on top of the wasm value stack
222
// ... Handle exception using values ...
223
//
224
// br_on_exn takes an exnref object and branches if it matches the given tag.
225
// There can be multiple br_on_exn instructions if we want to match for another
226
// tag, but for now we only test for __cpp_exception tag, and if it does not
227
// match, i.e., it is a foreign exception, we rethrow it.
228
//
229
// In the destination BB that's the target of br_on_exn, extracted exception
230
// values (in C++'s case a single i32, which represents an exception pointer)
231
// are placed on top of the wasm stack. Because we can't model wasm stack in
232
// LLVM instruction, we use 'extract_exception' pseudo instruction to retrieve
233
// it. The pseudo instruction will be deleted later.
234
42
bool WebAssemblyLateEHPrepare::addExceptionExtraction(MachineFunction &MF) {
235
42
  const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
236
42
  auto *EHInfo = MF.getWasmEHFuncInfo();
237
42
  SmallVector<MachineInstr *, 16> ExtractInstrs;
238
42
  SmallVector<MachineInstr *, 8> ToDelete;
239
252
  for (auto &MBB : MF) {
240
1.00k
    for (auto &MI : MBB) {
241
1.00k
      if (MI.getOpcode() == WebAssembly::EXTRACT_EXCEPTION_I32) {
242
56
        if (MI.getOperand(0).isDead())
243
15
          ToDelete.push_back(&MI);
244
41
        else
245
41
          ExtractInstrs.push_back(&MI);
246
56
      }
247
1.00k
    }
248
252
  }
249
42
  bool Changed = !ToDelete.empty() || 
!ExtractInstrs.empty()29
;
250
42
  for (auto *MI : ToDelete)
251
15
    MI->eraseFromParent();
252
42
  if (ExtractInstrs.empty())
253
15
    return Changed;
254
27
255
27
  // Find terminate pads.
256
27
  SmallSet<MachineBasicBlock *, 8> TerminatePads;
257
193
  for (auto &MBB : MF) {
258
793
    for (auto &MI : MBB) {
259
793
      if (MI.isCall()) {
260
162
        const MachineOperand &CalleeOp = MI.getOperand(0);
261
162
        if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
262
110
                                       WebAssembly::ClangCallTerminateFn)
263
5
          TerminatePads.insert(getMatchingEHPad(&MI));
264
162
      }
265
793
    }
266
193
  }
267
27
268
41
  for (auto *Extract : ExtractInstrs) {
269
41
    MachineBasicBlock *EHPad = getMatchingEHPad(Extract);
270
41
    assert(EHPad && "No matching EH pad for extract_exception");
271
41
    auto CatchPos = EHPad->begin();
272
41
    if (CatchPos->isEHLabel()) // EH pad starts with an EH label
273
41
      ++CatchPos;
274
41
    MachineInstr *Catch = &*CatchPos;
275
41
276
41
    if (Catch->getNextNode() != Extract)
277
4
      EHPad->insert(Catch->getNextNode(), Extract->removeFromParent());
278
41
279
41
    // - Before:
280
41
    // ehpad:
281
41
    //   %exnref:exnref = catch
282
41
    //   %exn:i32 = extract_exception
283
41
    //   ... use exn ...
284
41
    //
285
41
    // - After:
286
41
    // ehpad:
287
41
    //   %exnref:exnref = catch
288
41
    //   br_on_exn %thenbb, $__cpp_exception, %exnref
289
41
    //   br %elsebb
290
41
    // elsebb:
291
41
    //   rethrow
292
41
    // thenbb:
293
41
    //   %exn:i32 = extract_exception
294
41
    //   ... use exn ...
295
41
    unsigned ExnReg = Catch->getOperand(0).getReg();
296
41
    auto *ThenMBB = MF.CreateMachineBasicBlock();
297
41
    auto *ElseMBB = MF.CreateMachineBasicBlock();
298
41
    MF.insert(std::next(MachineFunction::iterator(EHPad)), ElseMBB);
299
41
    MF.insert(std::next(MachineFunction::iterator(ElseMBB)), ThenMBB);
300
41
    ThenMBB->splice(ThenMBB->end(), EHPad, Extract, EHPad->end());
301
41
    ThenMBB->transferSuccessors(EHPad);
302
41
    EHPad->addSuccessor(ThenMBB);
303
41
    EHPad->addSuccessor(ElseMBB);
304
41
305
41
    DebugLoc DL = Extract->getDebugLoc();
306
41
    const char *CPPExnSymbol = MF.createExternalSymbolName("__cpp_exception");
307
41
    BuildMI(EHPad, DL, TII.get(WebAssembly::BR_ON_EXN))
308
41
        .addMBB(ThenMBB)
309
41
        .addExternalSymbol(CPPExnSymbol)
310
41
        .addReg(ExnReg);
311
41
    BuildMI(EHPad, DL, TII.get(WebAssembly::BR)).addMBB(ElseMBB);
312
41
313
41
    // When this is a terminate pad with __clang_call_terminate() call, we don't
314
41
    // rethrow it anymore and call __clang_call_terminate() with a nullptr
315
41
    // argument, which will call std::terminate().
316
41
    //
317
41
    // - Before:
318
41
    // ehpad:
319
41
    //   %exnref:exnref = catch
320
41
    //   %exn:i32 = extract_exception
321
41
    //   call @__clang_call_terminate(%exn)
322
41
    //   unreachable
323
41
    //
324
41
    // - After:
325
41
    // ehpad:
326
41
    //   %exnref:exnref = catch
327
41
    //   br_on_exn %thenbb, $__cpp_exception, %exnref
328
41
    //   br %elsebb
329
41
    // elsebb:
330
41
    //   call @__clang_call_terminate(0)
331
41
    //   unreachable
332
41
    // thenbb:
333
41
    //   %exn:i32 = extract_exception
334
41
    //   call @__clang_call_terminate(%exn)
335
41
    //   unreachable
336
41
    if (TerminatePads.count(EHPad)) {
337
5
      Function *ClangCallTerminateFn =
338
5
          MF.getFunction().getParent()->getFunction(
339
5
              WebAssembly::ClangCallTerminateFn);
340
5
      assert(ClangCallTerminateFn &&
341
5
             "There is no __clang_call_terminate() function");
342
5
      BuildMI(ElseMBB, DL, TII.get(WebAssembly::CALL_VOID))
343
5
          .addGlobalAddress(ClangCallTerminateFn)
344
5
          .addImm(0);
345
5
      BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
346
5
347
36
    } else {
348
36
      BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg);
349
36
      if (EHInfo->hasEHPadUnwindDest(EHPad))
350
3
        ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad));
351
36
    }
352
41
  }
353
27
354
27
  return true;
355
27
}
356
357
// After the stack is unwound due to a thrown exception, the __stack_pointer
358
// global can point to an invalid address. This inserts instructions that
359
// restore __stack_pointer global.
360
42
bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
361
42
  const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(
362
42
      MF.getSubtarget().getFrameLowering());
363
42
  if (!FrameLowering->needsPrologForEH(MF))
364
1
    return false;
365
41
  bool Changed = false;
366
41
367
331
  for (auto &MBB : MF) {
368
331
    if (!MBB.isEHPad())
369
261
      continue;
370
70
    Changed = true;
371
70
372
70
    // Insert __stack_pointer restoring instructions at the beginning of each EH
373
70
    // pad, after the catch instruction. Here it is safe to assume that SP32
374
70
    // holds the latest value of __stack_pointer, because the only exception for
375
70
    // this case is when a function uses the red zone, but that only happens
376
70
    // with leaf functions, and we don't restore __stack_pointer in leaf
377
70
    // functions anyway.
378
70
    auto InsertPos = MBB.begin();
379
70
    if (InsertPos->isEHLabel()) // EH pad starts with an EH label
380
70
      ++InsertPos;
381
70
    if (InsertPos->getOpcode() == WebAssembly::CATCH)
382
70
      ++InsertPos;
383
70
    FrameLowering->writeSPToGlobal(WebAssembly::SP32, MF, MBB, InsertPos,
384
70
                                   MBB.begin()->getDebugLoc());
385
70
  }
386
41
  return Changed;
387
41
}