aboutsummaryrefslogtreecommitdiff
path: root/lib/MC/MCWin64EH.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MC/MCWin64EH.cpp')
-rw-r--r--lib/MC/MCWin64EH.cpp108
1 files changed, 96 insertions, 12 deletions
diff --git a/lib/MC/MCWin64EH.cpp b/lib/MC/MCWin64EH.cpp
index 0724b109e1a1..4e9a29667097 100644
--- a/lib/MC/MCWin64EH.cpp
+++ b/lib/MC/MCWin64EH.cpp
@@ -1,9 +1,8 @@
//===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// 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
//
//===----------------------------------------------------------------------===//
@@ -256,8 +255,12 @@ static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
MCSymbolRefExpr::create(RHS, Context), Context);
MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer);
+ // It should normally be possible to calculate the length of a function
+ // at this point, but it might not be possible in the presence of certain
+ // unusual constructs, like an inline asm with an alignment directive.
int64_t value;
- Diff->evaluateAsAbsolute(value, OS->getAssembler());
+ if (!Diff->evaluateAsAbsolute(value, OS->getAssembler()))
+ report_fatal_error("Failed to evaluate function length in SEH unwind info");
return value;
}
@@ -453,6 +456,38 @@ static void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
}
}
+// Returns the epilog symbol of an epilog with the exact same unwind code
+// sequence, if it exists. Otherwise, returns nulltpr.
+// EpilogInstrs - Unwind codes for the current epilog.
+// Epilogs - Epilogs that potentialy match the current epilog.
+static MCSymbol*
+FindMatchingEpilog(const std::vector<WinEH::Instruction>& EpilogInstrs,
+ const std::vector<MCSymbol *>& Epilogs,
+ const WinEH::FrameInfo *info) {
+ for (auto *EpilogStart : Epilogs) {
+ auto InstrsIter = info->EpilogMap.find(EpilogStart);
+ assert(InstrsIter != info->EpilogMap.end() &&
+ "Epilog not found in EpilogMap");
+ const auto &Instrs = InstrsIter->second;
+
+ if (Instrs.size() != EpilogInstrs.size())
+ continue;
+
+ bool Match = true;
+ for (unsigned i = 0; i < Instrs.size(); ++i)
+ if (Instrs[i].Operation != EpilogInstrs[i].Operation ||
+ Instrs[i].Offset != EpilogInstrs[i].Offset ||
+ Instrs[i].Register != EpilogInstrs[i].Register) {
+ Match = false;
+ break;
+ }
+
+ if (Match)
+ return EpilogStart;
+ }
+ return nullptr;
+}
+
// Populate the .xdata section. The format of .xdata on ARM64 is documented at
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
@@ -467,22 +502,71 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
streamer.EmitLabel(Label);
info->Symbol = Label;
- uint32_t FuncLength = 0x0;
- if (info->FuncletOrFuncEnd)
- FuncLength = (uint32_t)GetAbsDifference(streamer, info->FuncletOrFuncEnd,
- info->Begin);
- FuncLength /= 4;
+ int64_t RawFuncLength;
+ if (!info->FuncletOrFuncEnd) {
+ // FIXME: This is very wrong; we emit SEH data which covers zero bytes
+ // of code. But otherwise test/MC/AArch64/seh.s crashes.
+ RawFuncLength = 0;
+ } else {
+ // FIXME: GetAbsDifference tries to compute the length of the function
+ // immediately, before the whole file is emitted, but in general
+ // that's impossible: the size in bytes of certain assembler directives
+ // like .align and .fill is not known until the whole file is parsed and
+ // relaxations are applied. Currently, GetAbsDifference fails with a fatal
+ // error in that case. (We mostly don't hit this because inline assembly
+ // specifying those directives is rare, and we don't normally try to
+ // align loops on AArch64.)
+ //
+ // There are two potential approaches to delaying the computation. One,
+ // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000",
+ // as long as we have some conservative estimate we could use to prove
+ // that we don't need to split the unwind data. Emitting the constant
+ // is straightforward, but there's no existing code for estimating the
+ // size of the function.
+ //
+ // The other approach would be to use a dedicated, relaxable fragment,
+ // which could grow to accommodate splitting the unwind data if
+ // necessary. This is more straightforward, since it automatically works
+ // without any new infrastructure, and it's consistent with how we handle
+ // relaxation in other contexts. But it would require some refactoring
+ // to move parts of the pdata/xdata emission into the implementation of
+ // a fragment. We could probably continue to encode the unwind codes
+ // here, but we'd have to emit the pdata, the xdata header, and the
+ // epilogue scopes later, since they depend on whether the we need to
+ // split the unwind data.
+ RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd,
+ info->Begin);
+ }
+ if (RawFuncLength > 0xFFFFF)
+ report_fatal_error("SEH unwind data splitting not yet implemented");
+ uint32_t FuncLength = (uint32_t)RawFuncLength / 4;
uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
uint32_t TotalCodeBytes = PrologCodeBytes;
// Process epilogs.
MapVector<MCSymbol *, uint32_t> EpilogInfo;
+ // Epilogs processed so far.
+ std::vector<MCSymbol *> AddedEpilogs;
+
for (auto &I : info->EpilogMap) {
MCSymbol *EpilogStart = I.first;
auto &EpilogInstrs = I.second;
uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
- EpilogInfo[EpilogStart] = TotalCodeBytes;
- TotalCodeBytes += CodeBytes;
+
+ MCSymbol* MatchingEpilog =
+ FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
+ if (MatchingEpilog) {
+ assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
+ "Duplicate epilog not found");
+ EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
+ // Clear the unwind codes in the EpilogMap, so that they don't get output
+ // in the logic below.
+ EpilogInstrs.clear();
+ } else {
+ EpilogInfo[EpilogStart] = TotalCodeBytes;
+ TotalCodeBytes += CodeBytes;
+ AddedEpilogs.push_back(EpilogStart);
+ }
}
// Code Words, Epilog count, E, X, Vers, Function Length