aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/ExecutionEngine/JITLink/CompactUnwindSupport.cpp
blob: 51e3d26479ffdf54bd7c2e1f04e50859e98cae9f (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
//=------- CompactUnwindSupport.cpp - Compact Unwind format support -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Compact Unwind support.
//
//===----------------------------------------------------------------------===//

#include "CompactUnwindSupport.h"

#include "llvm/ADT/Sequence.h"

#define DEBUG_TYPE "jitlink"

namespace llvm {
namespace jitlink {

Error splitCompactUnwindBlocks(LinkGraph &G, Section &CompactUnwindSection,
                               size_t RecordSize) {

  std::vector<Block *> OriginalBlocks(CompactUnwindSection.blocks().begin(),
                                      CompactUnwindSection.blocks().end());
  LLVM_DEBUG({
    dbgs() << "In " << G.getName() << " splitting compact unwind section "
           << CompactUnwindSection.getName() << " containing "
           << OriginalBlocks.size() << " initial blocks...\n";
  });

  while (!OriginalBlocks.empty()) {
    auto *B = OriginalBlocks.back();
    OriginalBlocks.pop_back();

    if (B->getSize() == 0) {
      LLVM_DEBUG({
        dbgs() << "  Skipping empty block at "
               << formatv("{0:x16}", B->getAddress()) << "\n";
      });
      continue;
    }

    unsigned NumBlocks = B->getSize() / RecordSize;

    LLVM_DEBUG({
      dbgs() << "  Splitting block at " << formatv("{0:x16}", B->getAddress())
             << " into " << NumBlocks << " compact unwind record(s)\n";
    });

    if (B->getSize() % RecordSize)
      return make_error<JITLinkError>(
          "Error splitting compact unwind record in " + G.getName() +
          ": block at " + formatv("{0:x}", B->getAddress()) + " has size " +
          formatv("{0:x}", B->getSize()) +
          " (not a multiple of CU record size of " +
          formatv("{0:x}", RecordSize) + ")");

    auto Blocks =
        G.splitBlock(*B, map_range(seq(1U, NumBlocks), [=](Edge::OffsetT Idx) {
          return Idx * RecordSize;
        }));

    for (auto *CURec : Blocks) {
      bool AddedKeepAlive = false;

      for (auto &E : CURec->edges()) {
        if (E.getOffset() == 0) {
          LLVM_DEBUG({
            dbgs() << "    Updating compact unwind record at "
                   << CURec->getAddress() << " to point to "
                   << (E.getTarget().hasName() ? *E.getTarget().getName()
                                               : StringRef())
                   << " (at " << E.getTarget().getAddress() << ")\n";
          });

          if (E.getTarget().isExternal())
            return make_error<JITLinkError>(
                "Error adding keep-alive edge for compact unwind record at " +
                formatv("{0:x}", CURec->getAddress()) + ": target " +
                *E.getTarget().getName() + " is an external symbol");
          auto &TgtBlock = E.getTarget().getBlock();
          auto &CURecSym =
              G.addAnonymousSymbol(*CURec, 0, RecordSize, false, false);
          TgtBlock.addEdge(Edge::KeepAlive, 0, CURecSym, 0);
          AddedKeepAlive = true;
        }
      }

      if (!AddedKeepAlive)
        return make_error<JITLinkError>(
            "Error adding keep-alive edge for compact unwind record at " +
            formatv("{0:x}", CURec->getAddress()) +
            ": no outgoing target edge at offset 0");
    }
  }

  return Error::success();
}

} // end namespace jitlink
} // end namespace llvm