[llvm] 99e50e5 - [WinEH][ARM64] Split Unwind Info for Fucntions Larger than 1MB

Zhaoshi Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 5 12:01:13 PDT 2022


Author: Zhaoshi Zheng
Date: 2022-08-05T11:46:41-07:00
New Revision: 99e50e583867ac35ace36f5da50b3a3ff7c51d2e

URL: https://github.com/llvm/llvm-project/commit/99e50e583867ac35ace36f5da50b3a3ff7c51d2e
DIFF: https://github.com/llvm/llvm-project/commit/99e50e583867ac35ace36f5da50b3a3ff7c51d2e.diff

LOG: [WinEH][ARM64] Split Unwind Info for Fucntions Larger than 1MB

Create function segments and emit unwind info of them.

A segment must be less than 1MB and no prolog or epilog is splitted between two
segments.

This patch should generate correct, though not optimal, unwind info for large
functions. Currently it only generate pacted info (.pdata) only for functions
that are less than 1MB (single-segment functions). This is NFC from before this
patch.

The next step is to enable (.pdata) only unwind info for the first segment or
segments that have neither prolog or epilog in a multi-segment function.

Another future work item is to further split segments that require more than 255
code words or have more than 65535 epilogs.

Reference:
https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments

Differential Revision: https://reviews.llvm.org/D130049

Added: 
    llvm/test/MC/AArch64/seh-large-func-multi-epilog.s
    llvm/test/MC/AArch64/seh-large-func.s

Modified: 
    llvm/include/llvm/MC/MCWinEH.h
    llvm/lib/MC/MCWin64EH.cpp
    llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/MC/MCWinEH.h b/llvm/include/llvm/MC/MCWinEH.h
index c16396ea5e716..2c516f78efef2 100644
--- a/llvm/include/llvm/MC/MCWinEH.h
+++ b/llvm/include/llvm/MC/MCWinEH.h
@@ -46,6 +46,7 @@ struct FrameInfo {
   const MCSymbol *Symbol = nullptr;
   MCSection *TextSection = nullptr;
   uint32_t PackedInfo = 0;
+  uint32_t PrologCodeBytes = 0;
 
   bool HandlesUnwind = false;
   bool HandlesExceptions = false;
@@ -62,6 +63,21 @@ struct FrameInfo {
   };
   MapVector<MCSymbol *, Epilog> EpilogMap;
 
+  // For splitting unwind info of large functions
+  struct Segment {
+    int64_t Offset;
+    int64_t Length;
+    bool HasProlog;
+    MCSymbol *Symbol;
+    // Map an Epilog's symbol to its offset within the function.
+    MapVector<MCSymbol *, int64_t> Epilogs;
+
+    Segment(int64_t Offset, int64_t Length, bool HasProlog = false)
+        : Offset(Offset), Length(Length), HasProlog(HasProlog) {}
+  };
+
+  std::vector<Segment> Segments;
+
   FrameInfo() = default;
   FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel)
       : Begin(BeginFuncEHLabel), Function(Function) {}

diff  --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp
index ffabe0fe89788..b22c02d4e1fb9 100644
--- a/llvm/lib/MC/MCWin64EH.cpp
+++ b/llvm/lib/MC/MCWin64EH.cpp
@@ -128,6 +128,17 @@ static void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
   }
 }
 
+static void EmitSymbolRefWithOfs(MCStreamer &streamer,
+                                 const MCSymbol *Base,
+                                 int64_t Offset) {
+  MCContext &Context = streamer.getContext();
+  const MCConstantExpr *OffExpr = MCConstantExpr::create(Offset, Context);
+  const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base,
+                                              MCSymbolRefExpr::VK_COFF_IMGREL32,
+                                              Context);
+  streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, OffExpr, Context), 4);
+}
+
 static void EmitSymbolRefWithOfs(MCStreamer &streamer,
                                  const MCSymbol *Base,
                                  const MCSymbol *Other) {
@@ -642,26 +653,29 @@ getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
       return -1;
   }
 
-  // If the epilog was a subset of the prolog, find its offset.
   if (Epilog.size() == Prolog.size())
     return 0;
+
+  // If the epilog was a subset of the prolog, find its offset.
   return ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction>(
       &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
 }
 
 static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
+                                  WinEH::FrameInfo::Segment *Seg,
                                   int PrologCodeBytes) {
   // Can only pack if there's one single epilog
-  if (info->EpilogMap.size() != 1)
+  if (Seg->Epilogs.size() != 1)
     return -1;
 
+  MCSymbol *Sym = Seg->Epilogs.begin()->first;
   const std::vector<WinEH::Instruction> &Epilog =
-      info->EpilogMap.begin()->second.Instructions;
+      info->EpilogMap[Sym].Instructions;
 
   // Check that the epilog actually is at the very end of the function,
   // otherwise it can't be packed.
-  uint32_t DistanceFromEnd = (uint32_t)GetAbsDifference(
-      streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
+  uint32_t DistanceFromEnd =
+      (uint32_t)(Seg->Offset + Seg->Length - Seg->Epilogs.begin()->second);
   if (DistanceFromEnd / 4 != Epilog.size())
     return -1;
 
@@ -686,7 +700,7 @@ static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
 
   // As we choose to express the epilog as part of the prolog, remove the
   // epilog from the map, so we don't try to emit its opcodes.
-  info->EpilogMap.clear();
+  info->EpilogMap.erase(Sym);
   return Offset;
 }
 
@@ -923,111 +937,20 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
   return true;
 }
 
-// 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,
-                                bool TryPacked = true) {
-  // If this UNWIND_INFO already has a symbol, it's already been emitted.
-  if (info->Symbol)
-    return;
-  // If there's no unwind info here (not even a terminating UOP_End), the
-  // unwind info is considered bogus and skipped. If this was done in
-  // response to an explicit .seh_handlerdata, the associated trailing
-  // handler data is left orphaned in the xdata section.
-  if (info->empty()) {
-    info->EmitAttempted = true;
-    return;
-  }
-  if (info->EmitAttempted) {
-    // If we tried to emit unwind info before (due to an explicit
-    // .seh_handlerdata directive), but skipped it (because there was no
-    // valid information to emit at the time), and it later got valid unwind
-    // opcodes, we can't emit it here, because the trailing handler data
-    // was already emitted elsewhere in the xdata section.
-    streamer.getContext().reportError(
-        SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
-                     " skipped due to no unwind info at the time "
-                     "(.seh_handlerdata too early?), but the function later "
-                     "did get unwind info that can't be emitted");
-    return;
-  }
-
-  simplifyARM64Opcodes(info->Instructions, false);
-  for (auto &I : info->EpilogMap)
-    simplifyARM64Opcodes(I.second.Instructions, true);
-
-  MCContext &context = streamer.getContext();
-  MCSymbol *Label = context.createTempSymbol();
-
-  streamer.emitValueToAlignment(4);
-  streamer.emitLabel(Label);
-  info->Symbol = Label;
-
-  int64_t RawFuncLength;
-  if (!info->FuncletOrFuncEnd) {
-    report_fatal_error("FuncletOrFuncEnd not set");
-  } 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;
-
-  int PackedEpilogOffset =
-      checkARM64PackedEpilog(streamer, info, PrologCodeBytes);
+static void ARM64ProcessEpilogs(WinEH::FrameInfo *info,
+                                WinEH::FrameInfo::Segment *Seg,
+                                uint32_t &TotalCodeBytes,
+                                MapVector<MCSymbol *, uint32_t> &EpilogInfo) {
 
-  if (PackedEpilogOffset >= 0 &&
-      uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
-      !info->HandlesExceptions && FuncLength <= 0x7ff && TryPacked) {
-    // Matching prolog/epilog and no exception handlers; check if the
-    // prolog matches the patterns that can be described by the packed
-    // format.
-
-    // info->Symbol was already set even if we didn't actually write any
-    // unwind info there. Keep using that as indicator that this unwind
-    // info has been generated already.
+  std::vector<MCSymbol *> EpilogStarts;
+  for (auto &I : Seg->Epilogs)
+    EpilogStarts.push_back(I.first);
 
-    if (tryARM64PackedUnwind(info, FuncLength, PackedEpilogOffset))
-      return;
-  }
-
-  // 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.Instructions;
+  for (auto S : EpilogStarts) {
+    MCSymbol *EpilogStart = S;
+    auto &EpilogInstrs = info->EpilogMap[S].Instructions;
     uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
 
     MCSymbol* MatchingEpilog =
@@ -1038,13 +961,17 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
              "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.
+      // in ARM64EmitUnwindInfoForSegment().
       EpilogInstrs.clear();
     } else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions,
                                                       EpilogInstrs)) >= 0) {
       EpilogInfo[EpilogStart] = PrologOffset;
+      // If the segment doesn't have a prolog, an end_c will be emitted before
+      // prolog opcodes. So epilog start index in opcodes array is moved by 1.
+      if (!Seg->HasProlog)
+        EpilogInfo[EpilogStart] += 1;
       // Clear the unwind codes in the EpilogMap, so that they don't get output
-      // in the logic below.
+      // in ARM64EmitUnwindInfoForSegment().
       EpilogInstrs.clear();
     } else {
       EpilogInfo[EpilogStart] = TotalCodeBytes;
@@ -1052,6 +979,143 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
       AddedEpilogs.push_back(EpilogStart);
     }
   }
+}
+
+static void ARM64FindSegmentsInFunction(MCStreamer &streamer,
+                                        WinEH::FrameInfo *info,
+                                        int64_t RawFuncLength) {
+  struct EpilogStartEnd {
+    MCSymbol *Start;
+    int64_t Offset;
+    int64_t End;
+  };
+  // Record Start and End of each epilog.
+  SmallVector<struct EpilogStartEnd, 4> Epilogs;
+  for (auto &I : info->EpilogMap) {
+    MCSymbol *Start = I.first;
+    auto &Instrs = I.second.Instructions;
+    int64_t Offset = GetAbsDifference(streamer, Start, info->Begin);
+    assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) &&
+           "Epilogs should be monotonically ordered");
+    Epilogs.push_back({Start, Offset, Offset + (int64_t)Instrs.size() * 4});
+  }
+
+  unsigned E = 0;
+  int64_t SegLimit = 0xFFFFC;
+  int64_t SegOffset = 0;
+
+  if (RawFuncLength > SegLimit) {
+
+    int64_t RemainingLength = RawFuncLength;
+
+    while (RemainingLength > SegLimit) {
+      // Try divide the function into segments, requirements:
+      // 1. Segment length <= 0xFFFFC;
+      // 2. Each Prologue or Epilogue must be fully within a segment.
+      int64_t SegLength = SegLimit;
+      int64_t SegEnd = SegOffset + SegLength;
+      // Keep record on symbols and offsets of epilogs in this segment.
+      MapVector<MCSymbol *, int64_t> EpilogsInSegment;
+
+      while (E < Epilogs.size() && Epilogs[E].End < SegEnd) {
+        // Epilogs within current segment.
+        EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset;
+        ++E;
+      }
+
+      // At this point, we have:
+      // 1. Put all epilogs in segments already. No action needed here; or
+      // 2. Found an epilog that will cross segments boundry. We need to
+      //    move back current segment's end boundry, so the epilog is entirely
+      //    in the next segment; or
+      // 3. Left at least one epilog that is entirely after this segment.
+      //    It'll be handled by the next iteration, or the last segment.
+      if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd)
+        // Move back current Segment's end boundry.
+        SegLength = Epilogs[E].Offset - SegOffset;
+
+      auto Seg = WinEH::FrameInfo::Segment(
+          SegOffset, SegLength, /* HasProlog */!SegOffset);
+      Seg.Epilogs = std::move(EpilogsInSegment);
+      info->Segments.push_back(Seg);
+
+      SegOffset += SegLength;
+      RemainingLength -= SegLength;
+    }
+  }
+
+  // Add the last segment when RawFuncLength > 0xFFFFC,
+  // or the only segment otherwise.
+  auto LastSeg =
+      WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset,
+                                /* HasProlog */!SegOffset);
+  for (; E < Epilogs.size(); ++E)
+    LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset;
+  info->Segments.push_back(LastSeg);
+}
+
+static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer,
+                                          WinEH::FrameInfo *info,
+                                          WinEH::FrameInfo::Segment &Seg,
+                                          bool TryPacked = true) {
+  MCContext &context = streamer.getContext();
+  MCSymbol *Label = context.createTempSymbol();
+
+  streamer.emitValueToAlignment(4);
+  streamer.emitLabel(Label);
+  Seg.Symbol = Label;
+  // Use the 1st segemnt's label as function's.
+  if (Seg.Offset == 0)
+    info->Symbol = Label;
+
+  bool HasProlog = Seg.HasProlog;
+  bool HasEpilogs = (Seg.Epilogs.size() != 0);
+
+  uint32_t SegLength = (uint32_t)Seg.Length / 4;
+  uint32_t PrologCodeBytes = info->PrologCodeBytes;
+
+  int PackedEpilogOffset = HasEpilogs ?
+      checkARM64PackedEpilog(streamer, info, &Seg, PrologCodeBytes) : -1;
+
+  // TODO:
+  // 1. Enable packed unwind info (.pdata only) for multi-segment functions.
+  // 2. Emit packed unwind info (.pdata only) for segments that have neithor
+  //    prolog nor epilog.
+  if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 &&
+      uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
+      !info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) {
+    // Matching prolog/epilog and no exception handlers; check if the
+    // prolog matches the patterns that can be described by the packed
+    // format.
+
+    // info->Symbol was already set even if we didn't actually write any
+    // unwind info there. Keep using that as indicator that this unwind
+    // info has been generated already.
+    if (tryARM64PackedUnwind(info, SegLength, PackedEpilogOffset))
+      return;
+  }
+
+  // If the prolog is not in this segment, we need to emit an end_c, which takes
+  // 1 byte, before prolog unwind ops.
+  if (!HasProlog) {
+    PrologCodeBytes += 1;
+    if (PackedEpilogOffset >= 0)
+      PackedEpilogOffset += 1;
+    // If a segment has neither prolog nor epilog, "With full .xdata record,
+    // Epilog Count = 1. Epilog Start Index points to end_c."
+    // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
+    // TODO: We can remove this if testing shows zero epilog scope is ok with
+    //       MS unwinder.
+    if (!HasEpilogs)
+      // Pack the fake epilog into phantom prolog.
+      PackedEpilogOffset = 0;
+  }
+
+  uint32_t TotalCodeBytes = PrologCodeBytes;
+
+  // Process epilogs.
+  MapVector<MCSymbol *, uint32_t> EpilogInfo;
+  ARM64ProcessEpilogs(info, &Seg, TotalCodeBytes, EpilogInfo);
 
   // Code Words, Epilog count, E, X, Vers, Function Length
   uint32_t row1 = 0x0;
@@ -1060,7 +1124,7 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
   if (CodeWordsMod)
     CodeWords++;
   uint32_t EpilogCount =
-      PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
+      PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size();
   bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
   if (!ExtensionWord) {
     row1 |= (EpilogCount & 0x1F) << 22;
@@ -1070,14 +1134,17 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
     row1 |= 1 << 20;
   if (PackedEpilogOffset >= 0) // E
     row1 |= 1 << 21;
-  row1 |= FuncLength & 0x3FFFF;
+  row1 |= SegLength & 0x3FFFF;
   streamer.emitInt32(row1);
 
   // Extended Code Words, Extended Epilog Count
   if (ExtensionWord) {
     // FIXME: We should be able to split unwind info into multiple sections.
     if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
-      report_fatal_error("SEH unwind data splitting not yet implemented");
+      report_fatal_error(
+          "SEH unwind data splitting is only implemnted for large functions, "
+          "cases of too many code words or too many epilogs will be done later"
+      );
     uint32_t row2 = 0x0;
     row2 |= (CodeWords & 0xFF) << 16;
     row2 |= (EpilogCount & 0xFFFF);
@@ -1089,8 +1156,8 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
     for (auto &I : EpilogInfo) {
       MCSymbol *EpilogStart = I.first;
       uint32_t EpilogIndex = I.second;
-      uint32_t EpilogOffset =
-          (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin);
+      // Epilog offset within the Segment.
+      uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset);
       if (EpilogOffset)
         EpilogOffset /= 4;
       uint32_t row3 = EpilogOffset;
@@ -1099,17 +1166,23 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
     }
   }
 
+  // Note that even for segments that have no prolog, we still need to emit
+  // prolog unwinding opcodes so that the unwinder knows how to unwind from
+  // such a segment.
+  // The end_c opcode at the start indicates to the unwinder that the actual
+  // prolog is outside of the current segment, and the unwinder shouldn't try
+  // to check for unwinding from a partial prolog.
+  if (!HasProlog)
+    // Emit an end_c.
+    streamer.emitInt8((uint8_t)0xE5);
+
   // Emit prolog unwind instructions (in reverse order).
-  uint8_t numInst = info->Instructions.size();
-  for (uint8_t c = 0; c < numInst; ++c) {
-    WinEH::Instruction inst = info->Instructions.back();
-    info->Instructions.pop_back();
-    ARM64EmitUnwindCode(streamer, inst);
-  }
+  for (auto Inst : llvm::reverse(info->Instructions))
+    ARM64EmitUnwindCode(streamer, Inst);
 
   // Emit epilog unwind instructions
-  for (auto &I : info->EpilogMap) {
-    auto &EpilogInstrs = I.second.Instructions;
+  for (auto &I : Seg.Epilogs) {
+    auto &EpilogInstrs = info->EpilogMap[I.first].Instructions;
     for (const WinEH::Instruction &inst : EpilogInstrs)
       ARM64EmitUnwindCode(streamer, inst);
   }
@@ -1126,6 +1199,83 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
         4);
 }
 
+// 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,
+                                bool TryPacked = true) {
+  // If this UNWIND_INFO already has a symbol, it's already been emitted.
+  if (info->Symbol)
+    return;
+  // If there's no unwind info here (not even a terminating UOP_End), the
+  // unwind info is considered bogus and skipped. If this was done in
+  // response to an explicit .seh_handlerdata, the associated trailing
+  // handler data is left orphaned in the xdata section.
+  if (info->empty()) {
+    info->EmitAttempted = true;
+    return;
+  }
+  if (info->EmitAttempted) {
+    // If we tried to emit unwind info before (due to an explicit
+    // .seh_handlerdata directive), but skipped it (because there was no
+    // valid information to emit at the time), and it later got valid unwind
+    // opcodes, we can't emit it here, because the trailing handler data
+    // was already emitted elsewhere in the xdata section.
+    streamer.getContext().reportError(
+        SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
+                     " skipped due to no unwind info at the time "
+                     "(.seh_handlerdata too early?), but the function later "
+                     "did get unwind info that can't be emitted");
+    return;
+  }
+
+  simplifyARM64Opcodes(info->Instructions, false);
+  for (auto &I : info->EpilogMap)
+    simplifyARM64Opcodes(I.second.Instructions, true);
+
+  int64_t RawFuncLength;
+  if (!info->FuncletOrFuncEnd) {
+    report_fatal_error("FuncletOrFuncEnd not set");
+  } 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);
+  }
+
+  ARM64FindSegmentsInFunction(streamer, info, RawFuncLength);
+
+  info->PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
+  for (auto &S : info->Segments)
+    ARM64EmitUnwindInfoForSegment(streamer, info, S, TryPacked);
+
+  // Clear prolog instructions after unwind info is emitted for all segments.
+  info->Instructions.clear();
+}
+
 static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
   uint32_t Count = 0;
   for (const auto &I : Insns) {
@@ -2205,6 +2355,24 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
         4);
 }
 
+static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
+                                     const WinEH::FrameInfo *info) {
+  MCContext &context = streamer.getContext();
+
+  streamer.emitValueToAlignment(4);
+  for (auto &S : info->Segments) {
+    EmitSymbolRefWithOfs(streamer, info->Begin, S.Offset);
+    if (info->PackedInfo)
+      streamer.emitInt32(info->PackedInfo);
+    else
+      streamer.emitValue(
+          MCSymbolRefExpr::create(S.Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
+                                  context),
+          4);
+  }
+}
+
+
 static void ARMEmitRuntimeFunction(MCStreamer &streamer,
                                    const WinEH::FrameInfo *info) {
   MCContext &context = streamer.getContext();
@@ -2241,7 +2409,7 @@ void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
       continue;
     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
     Streamer.switchSection(PData);
-    ARMEmitRuntimeFunction(Streamer, Info);
+    ARM64EmitRuntimeFunction(Streamer, Info);
   }
 }
 

diff  --git a/llvm/test/MC/AArch64/seh-large-func-multi-epilog.s b/llvm/test/MC/AArch64/seh-large-func-multi-epilog.s
new file mode 100644
index 0000000000000..c2d7f94f7b11f
--- /dev/null
+++ b/llvm/test/MC/AArch64/seh-large-func-multi-epilog.s
@@ -0,0 +1,308 @@
+// This test checks that we emit unwind info correctly for epilogs that:
+// 1. mirror the prolog; or
+// 2. are subsequence at the end of the prolog; or
+// 3. neither of above two.
+// in the same segment. 
+// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s -o %t.o
+// RUN: llvm-readobj -S -r -u %t.o | FileCheck %s
+
+// CHECK:       Section {
+// CHECK:         Number: 4
+// CHECK-NEXT:    Name: .xdata (2E 78 64 61 74 61 00 00)
+// CHECK-NEXT:    VirtualSize: 0x0
+// CHECK-NEXT:    VirtualAddress: 0x0
+// CHECK-NEXT:    RawDataSize: 80
+// CHECK-NEXT:    PointerToRawData: 0x1251AC
+// CHECK-NEXT:    PointerToRelocations: 0x0
+// CHECK-NEXT:    PointerToLineNumbers: 0x0
+// CHECK-NEXT:    RelocationCount: 0
+// CHECK-NEXT:    LineNumberCount: 0
+// CHECK-NEXT:    Characteristics [ (0x40300040)
+// CHECK-NEXT:      IMAGE_SCN_ALIGN_4BYTES (0x300000)
+// CHECK-NEXT:      IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+// CHECK-NEXT:      IMAGE_SCN_MEM_READ (0x40000000)
+// CHECK-NEXT:    ]
+// CHECK-NEXT:  }
+// CHECK-NEXT:  Section {
+// CHECK-NEXT:    Number: 5
+// CHECK-NEXT:    Name: .pdata (2E 70 64 61 74 61 00 00)
+// CHECK-NEXT:    VirtualSize: 0x0
+// CHECK-NEXT:    VirtualAddress: 0x0
+// CHECK-NEXT:    RawDataSize: 16
+// CHECK-NEXT:    PointerToRawData: 0x1251FC
+// CHECK-NEXT:    PointerToRelocations: 0x12520C
+// CHECK-NEXT:    PointerToLineNumbers: 0x0
+// CHECK-NEXT:    RelocationCount: 4
+// CHECK-NEXT:    LineNumberCount: 0
+// CHECK-NEXT:    Characteristics [ (0x40300040)
+// CHECK-NEXT:      IMAGE_SCN_ALIGN_4BYTES (0x300000)
+// CHECK-NEXT:      IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+// CHECK-NEXT:      IMAGE_SCN_MEM_READ (0x40000000)
+// CHECK-NEXT:    ]
+// CHECK-NEXT:  }
+// CHECK-NEXT:]
+// CHECK-LABEL:Relocations [
+// CHECK-NEXT:  Section (1) .text {
+// CHECK-NEXT:    0x94 IMAGE_REL_ARM64_BRANCH26 foo (12)
+// CHECK-NEXT:    0x125068 IMAGE_REL_ARM64_BRANCH26 foo (12)
+// CHECK-NEXT:  }
+// CHECK-NEXT:  Section (5) .pdata {
+// CHECK-NEXT:    0x0 IMAGE_REL_ARM64_ADDR32NB .text (0)
+// CHECK-NEXT:    0x4 IMAGE_REL_ARM64_ADDR32NB .xdata (7)
+// CHECK-NEXT:    0x8 IMAGE_REL_ARM64_ADDR32NB .text (0)
+// CHECK-NEXT:    0xC IMAGE_REL_ARM64_ADDR32NB .xdata (7)
+// CHECK-NEXT:  }
+// CHECK-NEXT:]
+// CHECK-LABEL:UnwindInformation [
+// CHECK-NEXT:  RuntimeFunction {
+// CHECK-NEXT:    Function: multi_epilog (0x0)
+// CHECK-NEXT:    ExceptionRecord: .xdata (0x0)
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength: 1048572
+// CHECK-NEXT:      Version: 0
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: No
+// CHECK-NEXT:      EpilogueScopes: 3
+// CHECK-NEXT:      ByteCodeLength: 24
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0xe1                ; mov fp, sp
+// CHECK-NEXT:        0xca16              ; stp x27, x28, [sp, #176]
+// CHECK-NEXT:        0xc998              ; stp x25, x26, [sp, #192]
+// CHECK-NEXT:        0xc91a              ; stp x23, x24, [sp, #208]
+// CHECK-NEXT:        0xc89c              ; stp x21, x22, [sp, #224]
+// CHECK-NEXT:        0xc81e              ; stp x19, x20, [sp, #240]
+// CHECK-NEXT:        0x9f                ; stp x29, x30, [sp, #-256]!
+// CHECK-NEXT:        0xe4                ; end
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      EpilogueScopes [
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 38
+// CHECK-NEXT:          EpilogueStartIndex: 0
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xe1                ; mov sp, fp
+// CHECK-NEXT:            0xca16              ; ldp x27, x28, [sp, #176]
+// CHECK-NEXT:            0xc998              ; ldp x25, x26, [sp, #192]
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 46
+// CHECK-NEXT:          EpilogueStartIndex: 3
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xc998              ; ldp x25, x26, [sp, #192]
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 52
+// CHECK-NEXT:          EpilogueStartIndex: 13
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xe1                ; mov sp, fp
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+// CHECK-NEXT:  RuntimeFunction {
+// CHECK-NEXT:    Function: multi_epilog +0xFFFFC (0xFFFFC)
+// CHECK-NEXT:    ExceptionRecord: .xdata +0x28 (0x28)
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength: 151744
+// CHECK-NEXT:      Version: 0
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: No
+// CHECK-NEXT:      EpilogueScopes: 3
+// CHECK-NEXT:      ByteCodeLength: 24
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0xe5                ; end_c
+// CHECK-NEXT:        0xe1                ; mov fp, sp
+// CHECK-NEXT:        0xca16              ; stp x27, x28, [sp, #176]
+// CHECK-NEXT:        0xc998              ; stp x25, x26, [sp, #192]
+// CHECK-NEXT:        0xc91a              ; stp x23, x24, [sp, #208]
+// CHECK-NEXT:        0xc89c              ; stp x21, x22, [sp, #224]
+// CHECK-NEXT:        0xc81e              ; stp x19, x20, [sp, #240]
+// CHECK-NEXT:        0x9f                ; stp x29, x30, [sp, #-256]!
+// CHECK-NEXT:        0xe4                ; end
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      EpilogueScopes [
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 37916
+// CHECK-NEXT:          EpilogueStartIndex: 1
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xe1                ; mov sp, fp
+// CHECK-NEXT:            0xca16              ; ldp x27, x28, [sp, #176]
+// CHECK-NEXT:            0xc998              ; ldp x25, x26, [sp, #192]
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 37924
+// CHECK-NEXT:          EpilogueStartIndex: 4
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xc998              ; ldp x25, x26, [sp, #192]
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 37930
+// CHECK-NEXT:          EpilogueStartIndex: 14
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xe1                ; mov sp, fp
+// CHECK-NEXT:            0xc91a              ; ldp x23, x24, [sp, #208]
+// CHECK-NEXT:            0xc89c              ; ldp x21, x22, [sp, #224]
+// CHECK-NEXT:            0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:            0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:            0xe4                ; end
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+// CHECK-NEXT:]
+
+	.text
+	.global	multi_epilog
+	.p2align	2
+	.seh_proc multi_epilog
+multi_epilog:
+	stp	x29, lr, [sp, #-256]!
+	.seh_save_fplr_x 256
+	stp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x25, x26, [sp, #192]
+	.seh_save_regp x25, 192
+	stp	x27, x28, [sp, #176]
+	.seh_save_regp x27, 176
+	mov	x29, fp
+	.seh_set_fp
+	.seh_endprologue
+        .rept 30
+        nop
+        .endr
+	bl	foo
+// Epilogs 1, 2 and 3 are in the same segment as prolog.
+// epilog1 - mirroring prolog
+	.seh_startepilogue
+	mov	sp, x29
+	.seh_set_fp
+	stp	x27, x28, [sp, #176]
+	.seh_save_regp x27, 176
+	stp	x25, x26, [sp, #192]
+	.seh_save_regp x25, 192
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+// epilog2 - a subsequence at the end of prolog, can use prolog's opcodes.
+	.seh_startepilogue
+	stp	x25, x26, [sp, #192]
+	.seh_save_regp x25, 192
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+// epilog3 - cannot use prolog's opcode.
+	.seh_startepilogue
+	mov	sp, x29
+	.seh_set_fp
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+        .rept 300000
+        nop
+        .endr
+	bl	foo
+// Epilogs below are in a segment without prolog
+// epilog4 - mirroring prolog, its start index should be 1, counting the end_c. 
+	.seh_startepilogue
+	mov	sp, x29
+	.seh_set_fp
+	stp	x27, x28, [sp, #176]
+	.seh_save_regp x27, 176
+	stp	x25, x26, [sp, #192]
+	.seh_save_regp x25, 192
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+// epilog5 - same as epilog2, its start index should be: 1 + epilog2's index.
+	.seh_startepilogue
+	stp	x25, x26, [sp, #192]
+	.seh_save_regp x25, 192
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+// epilog6 - same as epilog3, cannot use prolog's opcode. Again its start index
+//           should be: 1 + epilog3's index.
+	.seh_startepilogue
+	mov	sp, x29
+	.seh_set_fp
+	stp	x23, x24, [sp, #208]
+	.seh_save_regp x23, 208
+	stp	x21, x22, [sp, #224]
+	.seh_save_regp x21, 224
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+	.seh_endfunclet
+	.seh_endproc

diff  --git a/llvm/test/MC/AArch64/seh-large-func.s b/llvm/test/MC/AArch64/seh-large-func.s
new file mode 100644
index 0000000000000..d9defe6b18743
--- /dev/null
+++ b/llvm/test/MC/AArch64/seh-large-func.s
@@ -0,0 +1,212 @@
+// This test checks that we emit unwind info correctly for functions
+// larger than 1MB.
+// RUN: llvm-mc -triple aarch64-pc-win32 -filetype=obj %s -o %t.o
+// RUN: llvm-readobj -S -r -u %t.o | FileCheck %s
+
+// CHECK:        Section {
+// CHECK:          Number: 4
+// CHECK-NEXT:     Name: .xdata (2E 78 64 61 74 61 00 00)
+// CHECK-NEXT:     VirtualSize: 0x0
+// CHECK-NEXT:     VirtualAddress: 0x0
+// CHECK-NEXT:     RawDataSize: 52
+// CHECK-NEXT:     PointerToRawData: 0x3D0A20
+// CHECK-NEXT:     PointerToRelocations: 0x0
+// CHECK-NEXT:     PointerToLineNumbers: 0x0
+// CHECK-NEXT:     RelocationCount: 0
+// CHECK-NEXT:     LineNumberCount: 0
+// CHECK-NEXT:     Characteristics [ (0x40300040)
+// CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES (0x300000)
+// CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+// CHECK-NEXT:       IMAGE_SCN_MEM_READ (0x40000000)
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section {
+// CHECK-NEXT:     Number: 5
+// CHECK-NEXT:     Name: .pdata (2E 70 64 61 74 61 00 00)
+// CHECK-NEXT:     VirtualSize: 0x0
+// CHECK-NEXT:     VirtualAddress: 0x0
+// CHECK-NEXT:     RawDataSize: 40
+// CHECK-NEXT:     PointerToRawData: 0x3D0A54
+// CHECK-NEXT:     PointerToRelocations: 0x3D0A7C
+// CHECK-NEXT:     PointerToLineNumbers: 0x0
+// CHECK-NEXT:     RelocationCount: 10
+// CHECK-NEXT:     LineNumberCount: 0
+// CHECK-NEXT:     Characteristics [ (0x40300040)
+// CHECK-NEXT:       IMAGE_SCN_ALIGN_4BYTES (0x300000)
+// CHECK-NEXT:       IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+// CHECK-NEXT:       IMAGE_SCN_MEM_READ (0x40000000)
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+// CHECK-LABEL: Relocations [
+// CHECK-NEXT:   Section (1) .text {
+// CHECK-NEXT:     0x186A04 IMAGE_REL_ARM64_BRANCH26 foo (14)
+// CHECK-NEXT:     0x3D091C IMAGE_REL_ARM64_BRANCH26 foo (14)
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section (5) .pdata {
+// CHECK-NEXT:     0x0 IMAGE_REL_ARM64_ADDR32NB .text (0)
+// CHECK-NEXT:     0x4 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
+// CHECK-NEXT:     0x8 IMAGE_REL_ARM64_ADDR32NB .text (0)
+// CHECK-NEXT:     0xC IMAGE_REL_ARM64_ADDR32NB .xdata (9)
+// CHECK-NEXT:     0x10 IMAGE_REL_ARM64_ADDR32NB $L.text_1 (2)
+// CHECK-NEXT:     0x14 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
+// CHECK-NEXT:     0x18 IMAGE_REL_ARM64_ADDR32NB $L.text_2 (3)
+// CHECK-NEXT:     0x1C IMAGE_REL_ARM64_ADDR32NB .xdata (9)
+// CHECK-NEXT:     0x20 IMAGE_REL_ARM64_ADDR32NB $L.text_3 (4)
+// CHECK-NEXT:     0x24 IMAGE_REL_ARM64_ADDR32NB .xdata (9)
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+// CHECK-LABEL: UnwindInformation [
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: a (0x0)
+// CHECK-NEXT:     ExceptionRecord: .xdata (0x0)
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 1048572
+// CHECK-NEXT:       Version: 0
+// CHECK-NEXT:       ExceptionData: No
+// CHECK-NEXT:       EpiloguePacked: No
+// CHECK-NEXT:       EpilogueScopes: 0
+// CHECK-NEXT:       ByteCodeLength: 4
+// CHECK-NEXT:       Prologue [
+// CHECK-NEXT:         0xd561              ; str x30, [sp, #-16]!
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: a +0xFFFFC (0xFFFFC)
+// CHECK-NEXT:     ExceptionRecord: .xdata +0x8 (0x8)
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 551444
+// CHECK-NEXT:       Version: 0
+// CHECK-NEXT:       ExceptionData: No
+// CHECK-NEXT:       EpiloguePacked: Yes
+// CHECK-NEXT:       EpilogueOffset: 1
+// CHECK-NEXT:       ByteCodeLength: 4
+// CHECK-NEXT:       Prologue [
+// CHECK-NEXT:         0xe5                ; end_c
+// CHECK-NEXT:         0xd561              ; str x30, [sp, #-16]!
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       Epilogue [
+// CHECK-NEXT:         0xd561              ; ldr x30, [sp], #16
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: b (0x186A10)
+// CHECK-NEXT:     ExceptionRecord: .xdata +0x10 (0x10)
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 1048572
+// CHECK-NEXT:       Version: 0
+// CHECK-NEXT:       ExceptionData: No
+// CHECK-NEXT:       EpiloguePacked: No
+// CHECK-NEXT:       EpilogueScopes: 0
+// CHECK-NEXT:       ByteCodeLength: 8
+// CHECK-NEXT:       Prologue [
+// CHECK-NEXT:         0xe1                ; mov fp, sp
+// CHECK-NEXT:         0xc81e              ; stp x19, x20, [sp, #240]
+// CHECK-NEXT:         0x9f                ; stp x29, x30, [sp, #-256]!
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: $L.text_2 +0x86A0C (0x286A0C)
+// CHECK-NEXT:     ExceptionRecord: .xdata +0x1C (0x1C)
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 1048572
+// CHECK-NEXT:       Version: 0
+// CHECK-NEXT:       ExceptionData: No
+// CHECK-NEXT:       EpiloguePacked: Yes
+// CHECK-NEXT:       EpilogueOffset: 0
+// CHECK-NEXT:       ByteCodeLength: 8
+// CHECK-NEXT:       Prologue [
+// CHECK-NEXT:         0xe5                ; end_c
+// CHECK-NEXT:         0xe1                ; mov fp, sp
+// CHECK-NEXT:         0xc81e              ; stp x19, x20, [sp, #240]
+// CHECK-NEXT:         0x9f                ; stp x29, x30, [sp, #-256]!
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: $L.text_3 +0x86A08 (0x386A08)
+// CHECK-NEXT:     ExceptionRecord: .xdata +0x28 (0x28)
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 302888
+// CHECK-NEXT:       Version: 0
+// CHECK-NEXT:       ExceptionData: No
+// CHECK-NEXT:       EpiloguePacked: Yes
+// CHECK-NEXT:       EpilogueOffset: 1
+// CHECK-NEXT:       ByteCodeLength: 8
+// CHECK-NEXT:       Prologue [
+// CHECK-NEXT:         0xe5                ; end_c
+// CHECK-NEXT:         0xe1                ; mov fp, sp
+// CHECK-NEXT:         0xc81e              ; stp x19, x20, [sp, #240]
+// CHECK-NEXT:         0x9f                ; stp x29, x30, [sp, #-256]!
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       Epilogue [
+// CHECK-NEXT:         0xe1                ; mov sp, fp
+// CHECK-NEXT:         0xc81e              ; ldp x19, x20, [sp, #240]
+// CHECK-NEXT:         0x9f                ; ldp x29, x30, [sp], #256
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+	.text
+// A simple function with an single epilog mirroring the prolog.
+	.global	a
+	.p2align	2
+	.seh_proc a
+a:
+	str	x30, [sp, #-16]!
+	.seh_save_reg_x	x30, 16
+	.seh_endprologue
+        .rept 400000
+        nop
+        .endr
+	bl	foo
+	.seh_startepilogue
+	ldr	x30, [sp], #16
+	.seh_save_reg_x	x30, 16
+	.seh_endepilogue
+	ret
+	.seh_endfunclet
+	.seh_endproc
+
+// Example 1 from https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
+	.global	b
+	.p2align	2
+	.seh_proc b
+b:
+	stp	x29, lr, [sp, #-256]!
+	.seh_save_fplr_x 256
+	stp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	mov	x29, fp
+	.seh_set_fp
+	.seh_endprologue
+        .rept 600000
+        nop
+        .endr
+	bl	foo
+	.seh_startepilogue
+	mov	sp, x29
+	.seh_set_fp
+	ldp	x19, x20, [sp, #240]
+	.seh_save_regp x19, 240
+	ldp	x29, lr, [sp], #256
+	.seh_save_fplr_x 256
+	.seh_endepilogue
+	ret
+	.seh_endfunclet
+	.seh_endproc

diff  --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index b7cbf353c43f1..6bb91c00fae9b 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -855,7 +855,7 @@ bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length,
                            bool Prologue) {
   SW.startLine() << format("0x%02x                ; end_c\n", OC[Offset]);
   ++Offset;
-  return true;
+  return false;
 }
 
 bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,


        


More information about the llvm-commits mailing list