[llvm] 6b75a35 - [ARM] [MC] Add support for writing ARM WinEH unwind info

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 1 01:26:36 PDT 2022


Author: Martin Storsjö
Date: 2022-06-01T11:25:48+03:00
New Revision: 6b75a3523ffd79bc03265469aeeedab26079026e

URL: https://github.com/llvm/llvm-project/commit/6b75a3523ffd79bc03265469aeeedab26079026e
DIFF: https://github.com/llvm/llvm-project/commit/6b75a3523ffd79bc03265469aeeedab26079026e.diff

LOG: [ARM] [MC] Add support for writing ARM WinEH unwind info

This includes .seh_* directives for generating it from assembly.
It is designed fairly similarly to the ARM64 handling.

For .seh_handler directives, such as
".seh_handler __C_specific_handler, @except" (which is supported
on x86_64 and aarch64 so far), the "@except" bit doesn't work in
ARM assembly, as '@' is used as a comment character (on all current
platforms).

Allow using '%' instead of '@' for this purpose. This convention
is used by GAS in similar contexts already,
e.g. [1]:

    Note on targets where the @ character is the start of a comment
    (eg ARM) then another character is used instead. For example the
    ARM port uses the % character.

In practice, this unfortunately means that all such .seh_handler
directives will need ifdefs for ARM.

Contrary to ARM64, on ARM, it's quite common that we can't evaluate
e.g. the function length at this point, due to instructions whose
length is finalized later. (Also, inline jump tables end with
a ".p2align 1".)

If unable to to evaluate the function length immediately, emit
it as an MCExpr instead. If we'd implement splitting the unwind
info for a function (which isn't implemented for ARM64 yet either),
we wouldn't know whether we need to split it though.

Avoid calling getFrameIndexOffset() on an unset
FuncInfo.UnwindHelpFrameIdx, to avoid triggering asserts in the
preexisting testcase CodeGen/ARM/Windows/wineh-basic.ll. (Once
MSVC exception handling is fully implemented, those changes
can be reverted.)

[1] https://sourceware.org/binutils/docs/as/Section.html#Section

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

Added: 
    llvm/test/MC/ARM/seh.s

Modified: 
    llvm/include/llvm/MC/MCStreamer.h
    llvm/include/llvm/MC/MCWin64EH.h
    llvm/include/llvm/MC/MCWinEH.h
    llvm/include/llvm/Support/Win64EH.h
    llvm/lib/CodeGen/AsmPrinter/WinException.cpp
    llvm/lib/MC/MCAsmStreamer.cpp
    llvm/lib/MC/MCParser/COFFAsmParser.cpp
    llvm/lib/MC/MCWin64EH.cpp
    llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
    llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
    llvm/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp
    llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h
    llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
    llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 69cbc62759bf7..e077d01ab877c 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -171,6 +171,17 @@ class ARMTargetStreamer : public MCTargetStreamer {
 
   void emitConstantPools() override;
 
+  virtual void emitARMWinCFIAllocStack(unsigned Size, bool Wide);
+  virtual void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide);
+  virtual void emitARMWinCFISaveSP(unsigned Reg);
+  virtual void emitARMWinCFISaveFRegs(unsigned First, unsigned Last);
+  virtual void emitARMWinCFISaveLR(unsigned Offset);
+  virtual void emitARMWinCFIPrologEnd(bool Fragment);
+  virtual void emitARMWinCFINop(bool Wide);
+  virtual void emitARMWinCFIEpilogStart(unsigned Condition);
+  virtual void emitARMWinCFIEpilogEnd();
+  virtual void emitARMWinCFICustom(unsigned Opcode);
+
   /// Reset any state between object emissions, i.e. the equivalent of
   /// MCStreamer's reset method.
   virtual void reset();

diff  --git a/llvm/include/llvm/MC/MCWin64EH.h b/llvm/include/llvm/MC/MCWin64EH.h
index 065161d1759e6..622a666b78dd2 100644
--- a/llvm/include/llvm/MC/MCWin64EH.h
+++ b/llvm/include/llvm/MC/MCWin64EH.h
@@ -57,13 +57,19 @@ class UnwindEmitter : public WinEH::UnwindEmitter {
                       bool HandlerData) const override;
 };
 
-class ARM64UnwindEmitter : public WinEH::UnwindEmitter {
+class ARMUnwindEmitter : public WinEH::UnwindEmitter {
 public:
   void Emit(MCStreamer &Streamer) const override;
   void EmitUnwindInfo(MCStreamer &Streamer, WinEH::FrameInfo *FI,
                       bool HandlerData) const override;
 };
 
+class ARM64UnwindEmitter : public WinEH::UnwindEmitter {
+public:
+  void Emit(MCStreamer &Streamer) const override;
+  void EmitUnwindInfo(MCStreamer &Streamer, WinEH::FrameInfo *FI,
+                      bool HandlerData) const override;
+};
 }
 } // end namespace llvm
 

diff  --git a/llvm/include/llvm/MC/MCWinEH.h b/llvm/include/llvm/MC/MCWinEH.h
index 045bf586d1f15..233ac2c50fafb 100644
--- a/llvm/include/llvm/MC/MCWinEH.h
+++ b/llvm/include/llvm/MC/MCWinEH.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_MC_MCWINEH_H
 #define LLVM_MC_MCWINEH_H
 
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
 #include <vector>
 
@@ -50,12 +51,14 @@ struct FrameInfo {
   bool HandlesUnwind = false;
   bool HandlesExceptions = false;
   bool EmitAttempted = false;
+  bool Fragment = false;
 
   int LastFrameInst = -1;
   const FrameInfo *ChainedParent = nullptr;
   std::vector<Instruction> Instructions;
   struct Epilog {
     std::vector<Instruction> Instructions;
+    unsigned Condition;
   };
   MapVector<MCSymbol *, Epilog> EpilogMap;
 

diff  --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h
index 9359fcb4286a9..31345beaa66a5 100644
--- a/llvm/include/llvm/Support/Win64EH.h
+++ b/llvm/include/llvm/Support/Win64EH.h
@@ -24,6 +24,9 @@ namespace Win64EH {
 /// UnwindOpcodes - Enumeration whose values specify a single operation in
 /// the prolog of a function.
 enum UnwindOpcodes {
+  // The following set of unwind opcodes is for x86_64.  They are documented at
+  // https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64.
+  // Some generic values from this set are used for other architectures too.
   UOP_PushNonVol = 0,
   UOP_AllocLarge,
   UOP_AllocSmall,
@@ -57,7 +60,38 @@ enum UnwindOpcodes {
   UOP_SaveNext,
   UOP_TrapFrame,
   UOP_Context,
-  UOP_ClearUnwoundToCall
+  UOP_ClearUnwoundToCall,
+  // The following set of unwind opcodes is for ARM.  They are documented at
+  // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
+
+  // Stack allocations use UOP_AllocSmall, UOP_AllocLarge from above, plus
+  // the following. AllocSmall, AllocLarge and AllocHuge represent a 16 bit
+  // instruction, while the WideAlloc* opcodes represent a 32 bit instruction.
+  // Small can represent a stack offset of 0x7f*4 (252) bytes, Medium can
+  // represent up to 0x3ff*4 (4092) bytes, Large up to 0xffff*4 (262140) bytes,
+  // and Huge up to 0xffffff*4 (67108860) bytes.
+  UOP_AllocHuge,
+  UOP_WideAllocMedium,
+  UOP_WideAllocLarge,
+  UOP_WideAllocHuge,
+
+  UOP_WideSaveRegMask,
+  UOP_SaveSP,
+  UOP_SaveRegsR4R7LR,
+  UOP_WideSaveRegsR4R11LR,
+  UOP_SaveFRegD8D15,
+  UOP_SaveRegMask,
+  UOP_SaveLR,
+  UOP_SaveFRegD0D15,
+  UOP_SaveFRegD16D31,
+  // Using UOP_Nop from above
+  UOP_WideNop,
+  // Using UOP_End from above
+  UOP_EndNop,
+  UOP_WideEndNop,
+  // A custom unspecified opcode, consisting of one or more bytes. This
+  // allows producing opcodes in the implementation defined/reserved range.
+  UOP_Custom,
 };
 
 /// UnwindCode - This union describes a single operation in a function prolog,

diff  --git a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
index e00cd83cbead3..cdbd112dd6d4c 100644
--- a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp
@@ -693,7 +693,12 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
   }
 
   int UnwindHelpOffset = 0;
-  if (Asm->MAI->usesWindowsCFI())
+  // TODO: The check for UnwindHelpFrameIdx against max() below (and the
+  // second check further below) can be removed if MS C++ unwinding is
+  // implemented for ARM, when test/CodeGen/ARM/Windows/wineh-basic.ll
+  // passes without the check.
+  if (Asm->MAI->usesWindowsCFI() &&
+      FuncInfo.UnwindHelpFrameIdx != std::numeric_limits<int>::max())
     UnwindHelpOffset =
         getFrameIndexOffset(FuncInfo.UnwindHelpFrameIdx, FuncInfo);
 
@@ -755,7 +760,8 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
   AddComment("IPToStateXData");
   OS.emitValue(create32bitRef(IPToStateXData), 4);
 
-  if (Asm->MAI->usesWindowsCFI()) {
+  if (Asm->MAI->usesWindowsCFI() &&
+      FuncInfo.UnwindHelpFrameIdx != std::numeric_limits<int>::max()) {
     AddComment("UnwindHelp");
     OS.emitInt32(UnwindHelpOffset);
   }

diff  --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 9042e2972aab7..cf9d025863f76 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -2069,10 +2069,14 @@ void MCAsmStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind,
 
   OS << "\t.seh_handler ";
   Sym->print(OS, MAI);
+  char Marker = '@';
+  const Triple &T = getContext().getTargetTriple();
+  if (T.getArch() == Triple::arm || T.getArch() == Triple::thumb)
+    Marker = '%';
   if (Unwind)
-    OS << ", @unwind";
+    OS << ", " << Marker << "unwind";
   if (Except)
-    OS << ", @except";
+    OS << ", " << Marker << "except";
   EmitEOL();
 }
 

diff  --git a/llvm/lib/MC/MCParser/COFFAsmParser.cpp b/llvm/lib/MC/MCParser/COFFAsmParser.cpp
index 9b3b8708fdf56..f57cecf261bee 100644
--- a/llvm/lib/MC/MCParser/COFFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/COFFAsmParser.cpp
@@ -699,8 +699,8 @@ bool COFFAsmParser::ParseSEHDirectiveEndProlog(StringRef, SMLoc Loc) {
 
 bool COFFAsmParser::ParseAtUnwindOrAtExcept(bool &unwind, bool &except) {
   StringRef identifier;
-  if (getLexer().isNot(AsmToken::At))
-    return TokError("a handler attribute must begin with '@'");
+  if (getLexer().isNot(AsmToken::At) && getLexer().isNot(AsmToken::Percent))
+    return TokError("a handler attribute must begin with '@' or '%'");
   SMLoc startLoc = getLexer().getLoc();
   Lex();
   if (getParser().parseIdentifier(identifier))

diff  --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp
index 8f9108a064f69..df3556e6c22f8 100644
--- a/llvm/lib/MC/MCWin64EH.cpp
+++ b/llvm/lib/MC/MCWin64EH.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/MC/MCWin64EH.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCExpr.h"
@@ -250,8 +251,21 @@ void llvm::Win64EH::UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
   ::EmitUnwindInfo(Streamer, info);
 }
 
-static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
-                                const MCSymbol *RHS) {
+static const MCExpr *GetSubDivExpr(MCStreamer &Streamer, const MCSymbol *LHS,
+                                   const MCSymbol *RHS, int Div) {
+  MCContext &Context = Streamer.getContext();
+  const MCExpr *Expr =
+      MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
+                              MCSymbolRefExpr::create(RHS, Context), Context);
+  if (Div != 1)
+    Expr = MCBinaryExpr::createDiv(Expr, MCConstantExpr::create(Div, Context),
+                                   Context);
+  return Expr;
+}
+
+static Optional<int64_t> GetOptionalAbsDifference(MCStreamer &Streamer,
+                                                  const MCSymbol *LHS,
+                                                  const MCSymbol *RHS) {
   MCContext &Context = Streamer.getContext();
   const MCExpr *Diff =
       MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
@@ -262,10 +276,18 @@ static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
   // unusual constructs, like an inline asm with an alignment directive.
   int64_t value;
   if (!Diff->evaluateAsAbsolute(value, OS->getAssembler()))
-    report_fatal_error("Failed to evaluate function length in SEH unwind info");
+    return None;
   return value;
 }
 
+static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
+                                const MCSymbol *RHS) {
+  Optional<int64_t> MaybeDiff = GetOptionalAbsDifference(Streamer, LHS, RHS);
+  if (!MaybeDiff)
+    report_fatal_error("Failed to evaluate function length in SEH unwind info");
+  return *MaybeDiff;
+}
+
 static uint32_t ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
   uint32_t Count = 0;
   for (const auto &I : Insns) {
@@ -1102,8 +1124,395 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
         4);
 }
 
-static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
-                                     const WinEH::FrameInfo *info) {
+static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
+  uint32_t Count = 0;
+  for (const auto &I : Insns) {
+    switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
+    default:
+      llvm_unreachable("Unsupported ARM unwind code");
+    case Win64EH::UOP_AllocSmall:
+      Count += 1;
+      break;
+    case Win64EH::UOP_AllocLarge:
+      Count += 3;
+      break;
+    case Win64EH::UOP_AllocHuge:
+      Count += 4;
+      break;
+    case Win64EH::UOP_WideAllocMedium:
+      Count += 2;
+      break;
+    case Win64EH::UOP_WideAllocLarge:
+      Count += 3;
+      break;
+    case Win64EH::UOP_WideAllocHuge:
+      Count += 4;
+      break;
+    case Win64EH::UOP_WideSaveRegMask:
+      Count += 2;
+      break;
+    case Win64EH::UOP_SaveSP:
+      Count += 1;
+      break;
+    case Win64EH::UOP_SaveRegsR4R7LR:
+      Count += 1;
+      break;
+    case Win64EH::UOP_WideSaveRegsR4R11LR:
+      Count += 1;
+      break;
+    case Win64EH::UOP_SaveFRegD8D15:
+      Count += 1;
+      break;
+    case Win64EH::UOP_SaveRegMask:
+      Count += 2;
+      break;
+    case Win64EH::UOP_SaveLR:
+      Count += 2;
+      break;
+    case Win64EH::UOP_SaveFRegD0D15:
+      Count += 2;
+      break;
+    case Win64EH::UOP_SaveFRegD16D31:
+      Count += 2;
+      break;
+    case Win64EH::UOP_Nop:
+    case Win64EH::UOP_WideNop:
+    case Win64EH::UOP_End:
+    case Win64EH::UOP_EndNop:
+    case Win64EH::UOP_WideEndNop:
+      Count += 1;
+      break;
+    case Win64EH::UOP_Custom: {
+      int J;
+      for (J = 3; J > 0; J--)
+        if (I.Offset & (0xffu << (8 * J)))
+          break;
+      Count += J + 1;
+      break;
+    }
+    }
+  }
+  return Count;
+}
+
+// Unwind opcode encodings and restrictions are documented at
+// https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
+static void ARMEmitUnwindCode(MCStreamer &streamer,
+                              const WinEH::Instruction &inst) {
+  uint32_t w, lr;
+  int i;
+  switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
+  default:
+    llvm_unreachable("Unsupported ARM unwind code");
+  case Win64EH::UOP_AllocSmall:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0x7f);
+    streamer.emitInt8(inst.Offset / 4);
+    break;
+  case Win64EH::UOP_WideSaveRegMask:
+    assert((inst.Register & ~0x5fff) == 0);
+    lr = (inst.Register >> 14) & 1;
+    w = 0x8000 | (inst.Register & 0x1fff) | (lr << 13);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_SaveSP:
+    assert(inst.Register <= 0x0f);
+    streamer.emitInt8(0xc0 | inst.Register);
+    break;
+  case Win64EH::UOP_SaveRegsR4R7LR:
+    assert(inst.Register >= 4 && inst.Register <= 7);
+    assert(inst.Offset <= 1);
+    streamer.emitInt8(0xd0 | (inst.Register - 4) | (inst.Offset << 2));
+    break;
+  case Win64EH::UOP_WideSaveRegsR4R11LR:
+    assert(inst.Register >= 8 && inst.Register <= 11);
+    assert(inst.Offset <= 1);
+    streamer.emitInt8(0xd8 | (inst.Register - 8) | (inst.Offset << 2));
+    break;
+  case Win64EH::UOP_SaveFRegD8D15:
+    assert(inst.Register >= 8 && inst.Register <= 15);
+    streamer.emitInt8(0xe0 | (inst.Register - 8));
+    break;
+  case Win64EH::UOP_WideAllocMedium:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0x3ff);
+    w = 0xe800 | (inst.Offset / 4);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_SaveRegMask:
+    assert((inst.Register & ~0x40ff) == 0);
+    lr = (inst.Register >> 14) & 1;
+    w = 0xec00 | (inst.Register & 0x0ff) | (lr << 8);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_SaveLR:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0x0f);
+    streamer.emitInt8(0xef);
+    streamer.emitInt8(inst.Offset / 4);
+    break;
+  case Win64EH::UOP_SaveFRegD0D15:
+    assert(inst.Register <= 15);
+    assert(inst.Offset <= 15);
+    assert(inst.Register <= inst.Offset);
+    streamer.emitInt8(0xf5);
+    streamer.emitInt8((inst.Register << 4) | inst.Offset);
+    break;
+  case Win64EH::UOP_SaveFRegD16D31:
+    assert(inst.Register >= 16 && inst.Register <= 31);
+    assert(inst.Offset >= 16 && inst.Offset <= 31);
+    assert(inst.Register <= inst.Offset);
+    streamer.emitInt8(0xf6);
+    streamer.emitInt8(((inst.Register - 16) << 4) | (inst.Offset - 16));
+    break;
+  case Win64EH::UOP_AllocLarge:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0xffff);
+    w = inst.Offset / 4;
+    streamer.emitInt8(0xf7);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_AllocHuge:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0xffffff);
+    w = inst.Offset / 4;
+    streamer.emitInt8(0xf8);
+    streamer.emitInt8((w >> 16) & 0xff);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_WideAllocLarge:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0xffff);
+    w = inst.Offset / 4;
+    streamer.emitInt8(0xf9);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_WideAllocHuge:
+    assert((inst.Offset & 3) == 0);
+    assert(inst.Offset / 4 <= 0xffffff);
+    w = inst.Offset / 4;
+    streamer.emitInt8(0xfa);
+    streamer.emitInt8((w >> 16) & 0xff);
+    streamer.emitInt8((w >> 8) & 0xff);
+    streamer.emitInt8((w >> 0) & 0xff);
+    break;
+  case Win64EH::UOP_Nop:
+    streamer.emitInt8(0xfb);
+    break;
+  case Win64EH::UOP_WideNop:
+    streamer.emitInt8(0xfc);
+    break;
+  case Win64EH::UOP_EndNop:
+    streamer.emitInt8(0xfd);
+    break;
+  case Win64EH::UOP_WideEndNop:
+    streamer.emitInt8(0xfe);
+    break;
+  case Win64EH::UOP_End:
+    streamer.emitInt8(0xff);
+    break;
+  case Win64EH::UOP_Custom:
+    for (i = 3; i > 0; i--)
+      if (inst.Offset & (0xffu << (8 * i)))
+        break;
+    for (; i >= 0; i--)
+      streamer.emitInt8((inst.Offset >> (8 * i)) & 0xff);
+    break;
+  }
+}
+
+// Populate the .xdata section.  The format of .xdata on ARM is documented at
+// https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
+static void ARMEmitUnwindInfo(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;
+  }
+
+  MCContext &context = streamer.getContext();
+  MCSymbol *Label = context.createTempSymbol();
+
+  streamer.emitValueToAlignment(4);
+  streamer.emitLabel(Label);
+  info->Symbol = Label;
+
+  Optional<int64_t> RawFuncLength;
+  const MCExpr *FuncLengthExpr = nullptr;
+  if (!info->FuncletOrFuncEnd) {
+    report_fatal_error("FuncletOrFuncEnd not set");
+  } else {
+    // As the size of many thumb2 instructions isn't known until later,
+    // we can't always rely on being able to calculate the absolute
+    // length of the function here. If we can't calculate it, defer it
+    // to a relocation.
+    //
+    // In such a case, we won't know if the function is too long so that
+    // the unwind info would need to be split (but this isn't implemented
+    // anyway).
+    RawFuncLength =
+        GetOptionalAbsDifference(streamer, info->FuncletOrFuncEnd, info->Begin);
+    if (!RawFuncLength)
+      FuncLengthExpr =
+          GetSubDivExpr(streamer, info->FuncletOrFuncEnd, info->Begin, 2);
+  }
+  uint32_t FuncLength = 0;
+  if (RawFuncLength)
+    FuncLength = (uint32_t)*RawFuncLength / 2;
+  if (FuncLength > 0x3FFFF)
+    report_fatal_error("SEH unwind data splitting not yet implemented");
+  uint32_t PrologCodeBytes = ARMCountOfUnwindCodes(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.Instructions;
+    uint32_t CodeBytes = ARMCountOfUnwindCodes(EpilogInstrs);
+
+    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, F, E, X, Vers, Function Length
+  uint32_t row1 = 0x0;
+  uint32_t CodeWords = TotalCodeBytes / 4;
+  uint32_t CodeWordsMod = TotalCodeBytes % 4;
+  if (CodeWordsMod)
+    CodeWords++;
+  uint32_t EpilogCount = info->EpilogMap.size();
+  bool ExtensionWord = EpilogCount > 31 || CodeWords > 15;
+  if (!ExtensionWord) {
+    row1 |= (EpilogCount & 0x1F) << 23;
+    row1 |= (CodeWords & 0x0F) << 28;
+  }
+  if (info->HandlesExceptions) // X
+    row1 |= 1 << 20;
+  if (info->Fragment) // F
+    row1 |= 1 << 22;
+  row1 |= FuncLength & 0x3FFFF;
+  if (RawFuncLength)
+    streamer.emitInt32(row1);
+  else
+    streamer.emitValue(
+        MCBinaryExpr::createOr(FuncLengthExpr,
+                               MCConstantExpr::create(row1, context), context),
+        4);
+
+  // 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");
+    uint32_t row2 = 0x0;
+    row2 |= (CodeWords & 0xFF) << 16;
+    row2 |= (EpilogCount & 0xFFFF);
+    streamer.emitInt32(row2);
+  }
+
+  // Epilog Start Index, Epilog Start Offset
+  for (auto &I : EpilogInfo) {
+    MCSymbol *EpilogStart = I.first;
+    uint32_t EpilogIndex = I.second;
+
+    Optional<int64_t> MaybeEpilogOffset =
+        GetOptionalAbsDifference(streamer, EpilogStart, info->Begin);
+    const MCExpr *OffsetExpr = nullptr;
+    uint32_t EpilogOffset = 0;
+    if (MaybeEpilogOffset)
+      EpilogOffset = *MaybeEpilogOffset / 2;
+    else
+      OffsetExpr = GetSubDivExpr(streamer, EpilogStart, info->Begin, 2);
+
+    assert(info->EpilogMap.find(EpilogStart) != info->EpilogMap.end());
+    unsigned Condition = info->EpilogMap[EpilogStart].Condition;
+    assert(Condition <= 0xf);
+
+    uint32_t row3 = EpilogOffset;
+    row3 |= Condition << 20;
+    row3 |= (EpilogIndex & 0x3FF) << 24;
+    if (MaybeEpilogOffset)
+      streamer.emitInt32(row3);
+    else
+      streamer.emitValue(
+          MCBinaryExpr::createOr(
+              OffsetExpr, MCConstantExpr::create(row3, context), context),
+          4);
+  }
+
+  // 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();
+    ARMEmitUnwindCode(streamer, inst);
+  }
+
+  // Emit epilog unwind instructions
+  for (auto &I : info->EpilogMap) {
+    auto &EpilogInstrs = I.second.Instructions;
+    for (uint32_t i = 0; i < EpilogInstrs.size(); i++) {
+      WinEH::Instruction inst = EpilogInstrs[i];
+      ARMEmitUnwindCode(streamer, inst);
+    }
+  }
+
+  int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
+  assert(BytesMod >= 0);
+  for (int i = 0; i < BytesMod; i++)
+    streamer.emitInt8(0xFB);
+
+  if (info->HandlesExceptions)
+    streamer.emitValue(
+        MCSymbolRefExpr::create(info->ExceptionHandler,
+                                MCSymbolRefExpr::VK_COFF_IMGREL32, context),
+        4);
+}
+
+static void ARMEmitRuntimeFunction(MCStreamer &streamer,
+                                   const WinEH::FrameInfo *info) {
   MCContext &context = streamer.getContext();
 
   streamer.emitValueToAlignment(4);
@@ -1138,7 +1547,7 @@ void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
       continue;
     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
     Streamer.SwitchSection(PData);
-    ARM64EmitRuntimeFunction(Streamer, Info);
+    ARMEmitRuntimeFunction(Streamer, Info);
   }
 }
 
@@ -1161,3 +1570,48 @@ void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
   Streamer.SwitchSection(XData);
   ARM64EmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
 }
+
+void llvm::Win64EH::ARMUnwindEmitter::Emit(MCStreamer &Streamer) const {
+  // Emit the unwind info structs first.
+  for (const auto &CFI : Streamer.getWinFrameInfos()) {
+    WinEH::FrameInfo *Info = CFI.get();
+    if (Info->empty())
+      continue;
+    MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
+    Streamer.SwitchSection(XData);
+    ARMEmitUnwindInfo(Streamer, Info);
+  }
+
+  // Now emit RUNTIME_FUNCTION entries.
+  for (const auto &CFI : Streamer.getWinFrameInfos()) {
+    WinEH::FrameInfo *Info = CFI.get();
+    // ARMEmitUnwindInfo above clears the info struct, so we can't check
+    // empty here. But if a Symbol is set, we should create the corresponding
+    // pdata entry.
+    if (!Info->Symbol)
+      continue;
+    MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
+    Streamer.SwitchSection(PData);
+    ARMEmitRuntimeFunction(Streamer, Info);
+  }
+}
+
+void llvm::Win64EH::ARMUnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
+                                                     WinEH::FrameInfo *info,
+                                                     bool HandlerData) const {
+  // Called if there's an .seh_handlerdata directive before the end of the
+  // function. This forces writing the xdata record already here - and
+  // in this case, the function isn't actually ended already, but the xdata
+  // record needs to know the function length. In these cases, if the funclet
+  // end hasn't been marked yet, the xdata function length won't cover the
+  // whole function, only up to this point.
+  if (!info->FuncletOrFuncEnd) {
+    Streamer.SwitchSection(info->TextSection);
+    info->FuncletOrFuncEnd = Streamer.emitCFILabel();
+  }
+  // Switch sections (the static function above is meant to be called from
+  // here and from Emit().
+  MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
+  Streamer.SwitchSection(XData);
+  ARMEmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
+}

diff  --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index 01c8d402c0d7c..899643fcae720 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -453,6 +453,7 @@ class ARMAsmParser : public MCTargetAsmParser {
                          bool AllowRAAC = false);
   bool parseMemory(OperandVector &);
   bool parseOperand(OperandVector &, StringRef Mnemonic);
+  bool parseImmExpr(int64_t &Out);
   bool parsePrefix(ARMMCExpr::VariantKind &RefKind);
   bool parseMemRegOffsetShift(ARM_AM::ShiftOpc &ShiftType,
                               unsigned &ShiftAmount);
@@ -488,6 +489,17 @@ class ARMAsmParser : public MCTargetAsmParser {
   bool parseDirectiveAlign(SMLoc L);
   bool parseDirectiveThumbSet(SMLoc L);
 
+  bool parseDirectiveSEHAllocStack(SMLoc L, bool Wide);
+  bool parseDirectiveSEHSaveRegs(SMLoc L, bool Wide);
+  bool parseDirectiveSEHSaveSP(SMLoc L);
+  bool parseDirectiveSEHSaveFRegs(SMLoc L);
+  bool parseDirectiveSEHSaveLR(SMLoc L);
+  bool parseDirectiveSEHPrologEnd(SMLoc L, bool Fragment);
+  bool parseDirectiveSEHNop(SMLoc L, bool Wide);
+  bool parseDirectiveSEHEpilogStart(SMLoc L, bool Condition);
+  bool parseDirectiveSEHEpilogEnd(SMLoc L);
+  bool parseDirectiveSEHCustom(SMLoc L);
+
   bool isMnemonicVPTPredicable(StringRef Mnemonic, StringRef ExtraToken);
   StringRef splitMnemonic(StringRef Mnemonic, StringRef ExtraToken,
                           unsigned &PredicationCode,
@@ -6317,6 +6329,18 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) {
   }
 }
 
+bool ARMAsmParser::parseImmExpr(int64_t &Out) {
+  const MCExpr *Expr = nullptr;
+  SMLoc L = getParser().getTok().getLoc();
+  if (check(getParser().parseExpression(Expr), L, "expected expression"))
+    return true;
+  const MCConstantExpr *Value = dyn_cast_or_null<MCConstantExpr>(Expr);
+  if (check(!Value, L, "expected constant expression"))
+    return true;
+  Out = Value->getValue();
+  return false;
+}
+
 // parsePrefix - Parse ARM 16-bit relocations expression prefix, i.e.
 //  :lower16: and :upper16:.
 bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) {
@@ -11082,6 +11106,39 @@ bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) {
       parseDirectiveTLSDescSeq(DirectiveID.getLoc());
     else
       return true;
+  } else if (IsCOFF) {
+    if (IDVal == ".seh_stackalloc")
+      parseDirectiveSEHAllocStack(DirectiveID.getLoc(), /*Wide=*/false);
+    else if (IDVal == ".seh_stackalloc_w")
+      parseDirectiveSEHAllocStack(DirectiveID.getLoc(), /*Wide=*/true);
+    else if (IDVal == ".seh_save_regs")
+      parseDirectiveSEHSaveRegs(DirectiveID.getLoc(), /*Wide=*/false);
+    else if (IDVal == ".seh_save_regs_w")
+      parseDirectiveSEHSaveRegs(DirectiveID.getLoc(), /*Wide=*/true);
+    else if (IDVal == ".seh_save_sp")
+      parseDirectiveSEHSaveSP(DirectiveID.getLoc());
+    else if (IDVal == ".seh_save_fregs")
+      parseDirectiveSEHSaveFRegs(DirectiveID.getLoc());
+    else if (IDVal == ".seh_save_lr")
+      parseDirectiveSEHSaveLR(DirectiveID.getLoc());
+    else if (IDVal == ".seh_endprologue")
+      parseDirectiveSEHPrologEnd(DirectiveID.getLoc(), /*Fragment=*/false);
+    else if (IDVal == ".seh_endprologue_fragment")
+      parseDirectiveSEHPrologEnd(DirectiveID.getLoc(), /*Fragment=*/true);
+    else if (IDVal == ".seh_nop")
+      parseDirectiveSEHNop(DirectiveID.getLoc(), /*Wide=*/false);
+    else if (IDVal == ".seh_nop_w")
+      parseDirectiveSEHNop(DirectiveID.getLoc(), /*Wide=*/true);
+    else if (IDVal == ".seh_startepilogue")
+      parseDirectiveSEHEpilogStart(DirectiveID.getLoc(), /*Condition=*/false);
+    else if (IDVal == ".seh_startepilogue_cond")
+      parseDirectiveSEHEpilogStart(DirectiveID.getLoc(), /*Condition=*/true);
+    else if (IDVal == ".seh_endepilogue")
+      parseDirectiveSEHEpilogEnd(DirectiveID.getLoc());
+    else if (IDVal == ".seh_custom")
+      parseDirectiveSEHCustom(DirectiveID.getLoc());
+    else
+      return true;
   } else
     return true;
   return false;
@@ -12018,6 +12075,177 @@ bool ARMAsmParser::parseDirectiveThumbSet(SMLoc L) {
   return false;
 }
 
+/// parseDirectiveSEHAllocStack
+/// ::= .seh_stackalloc
+/// ::= .seh_stackalloc_w
+bool ARMAsmParser::parseDirectiveSEHAllocStack(SMLoc L, bool Wide) {
+  int64_t Size;
+  if (parseImmExpr(Size))
+    return true;
+  getTargetStreamer().emitARMWinCFIAllocStack(Size, Wide);
+  return false;
+}
+
+/// parseDirectiveSEHSaveRegs
+/// ::= .seh_save_regs
+/// ::= .seh_save_regs_w
+bool ARMAsmParser::parseDirectiveSEHSaveRegs(SMLoc L, bool Wide) {
+  SmallVector<std::unique_ptr<MCParsedAsmOperand>, 1> Operands;
+
+  if (parseRegisterList(Operands) ||
+      parseToken(AsmToken::EndOfStatement, "unexpected token in directive"))
+    return true;
+  ARMOperand &Op = (ARMOperand &)*Operands[0];
+  if (!Op.isRegList())
+    return Error(L, ".seh_save_regs{_w} expects GPR registers");
+  const SmallVectorImpl<unsigned> &RegList = Op.getRegList();
+  uint32_t Mask = 0;
+  for (size_t i = 0; i < RegList.size(); ++i) {
+    unsigned Reg = MRI->getEncodingValue(RegList[i]);
+    if (Reg == 15) // pc -> lr
+      Reg = 14;
+    if (Reg == 13)
+      return Error(L, ".seh_save_regs{_w} can't include SP");
+    assert(Reg < 16U && "Register out of range");
+    unsigned Bit = (1u << Reg);
+    Mask |= Bit;
+  }
+  if (!Wide && (Mask & 0x1f00) != 0)
+    return Error(L,
+                 ".seh_save_regs cannot save R8-R12, needs .seh_save_regs_w");
+  getTargetStreamer().emitARMWinCFISaveRegMask(Mask, Wide);
+  return false;
+}
+
+/// parseDirectiveSEHSaveSP
+/// ::= .seh_save_sp
+bool ARMAsmParser::parseDirectiveSEHSaveSP(SMLoc L) {
+  int Reg = tryParseRegister();
+  if (Reg == -1 || !MRI->getRegClass(ARM::GPRRegClassID).contains(Reg))
+    return Error(L, "expected GPR");
+  unsigned Index = MRI->getEncodingValue(Reg);
+  if (Index > 14 || Index == 13)
+    return Error(L, "invalid register for .seh_save_sp");
+  getTargetStreamer().emitARMWinCFISaveSP(Index);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFRegs
+/// ::= .seh_save_fregs
+bool ARMAsmParser::parseDirectiveSEHSaveFRegs(SMLoc L) {
+  SmallVector<std::unique_ptr<MCParsedAsmOperand>, 1> Operands;
+
+  if (parseRegisterList(Operands) ||
+      parseToken(AsmToken::EndOfStatement, "unexpected token in directive"))
+    return true;
+  ARMOperand &Op = (ARMOperand &)*Operands[0];
+  if (!Op.isDPRRegList())
+    return Error(L, ".seh_save_fregs expects DPR registers");
+  const SmallVectorImpl<unsigned> &RegList = Op.getRegList();
+  uint32_t Mask = 0;
+  for (size_t i = 0; i < RegList.size(); ++i) {
+    unsigned Reg = MRI->getEncodingValue(RegList[i]);
+    assert(Reg < 32U && "Register out of range");
+    unsigned Bit = (1u << Reg);
+    Mask |= Bit;
+  }
+
+  if (Mask == 0)
+    return Error(L, ".seh_save_fregs missing registers");
+
+  unsigned First = 0;
+  while ((Mask & 1) == 0) {
+    First++;
+    Mask >>= 1;
+  }
+  if (((Mask + 1) & Mask) != 0)
+    return Error(L,
+                 ".seh_save_fregs must take a contiguous range of registers");
+  unsigned Last = First;
+  while ((Mask & 2) != 0) {
+    Last++;
+    Mask >>= 1;
+  }
+  if (First < 16 && Last >= 16)
+    return Error(L, ".seh_save_fregs must be all d0-d15 or d16-d31");
+  getTargetStreamer().emitARMWinCFISaveFRegs(First, Last);
+  return false;
+}
+
+/// parseDirectiveSEHSaveLR
+/// ::= .seh_save_lr
+bool ARMAsmParser::parseDirectiveSEHSaveLR(SMLoc L) {
+  int64_t Offset;
+  if (parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().emitARMWinCFISaveLR(Offset);
+  return false;
+}
+
+/// parseDirectiveSEHPrologEnd
+/// ::= .seh_endprologue
+/// ::= .seh_endprologue_fragment
+bool ARMAsmParser::parseDirectiveSEHPrologEnd(SMLoc L, bool Fragment) {
+  getTargetStreamer().emitARMWinCFIPrologEnd(Fragment);
+  return false;
+}
+
+/// parseDirectiveSEHNop
+/// ::= .seh_nop
+/// ::= .seh_nop_w
+bool ARMAsmParser::parseDirectiveSEHNop(SMLoc L, bool Wide) {
+  getTargetStreamer().emitARMWinCFINop(Wide);
+  return false;
+}
+
+/// parseDirectiveSEHEpilogStart
+/// ::= .seh_startepilogue
+/// ::= .seh_startepilogue_cond
+bool ARMAsmParser::parseDirectiveSEHEpilogStart(SMLoc L, bool Condition) {
+  unsigned CC = ARMCC::AL;
+  if (Condition) {
+    MCAsmParser &Parser = getParser();
+    SMLoc S = Parser.getTok().getLoc();
+    const AsmToken &Tok = Parser.getTok();
+    if (!Tok.is(AsmToken::Identifier))
+      return Error(S, ".seh_startepilogue_cond missing condition");
+    CC = ARMCondCodeFromString(Tok.getString());
+    if (CC == ~0U)
+      return Error(S, "invalid condition");
+    Parser.Lex(); // Eat the token.
+  }
+
+  getTargetStreamer().emitARMWinCFIEpilogStart(CC);
+  return false;
+}
+
+/// parseDirectiveSEHEpilogEnd
+/// ::= .seh_endepilogue
+bool ARMAsmParser::parseDirectiveSEHEpilogEnd(SMLoc L) {
+  getTargetStreamer().emitARMWinCFIEpilogEnd();
+  return false;
+}
+
+/// parseDirectiveSEHCustom
+/// ::= .seh_custom
+bool ARMAsmParser::parseDirectiveSEHCustom(SMLoc L) {
+  unsigned Opcode = 0;
+  do {
+    int64_t Byte;
+    if (parseImmExpr(Byte))
+      return true;
+    if (Byte > 0xff || Byte < 0)
+      return Error(L, "Invalid byte value in .seh_custom");
+    if (Opcode > 0x00ffffff)
+      return Error(L, "Too many bytes in .seh_custom");
+    // Store the bytes as one big endian number in Opcode. In a multi byte
+    // opcode sequence, the first byte can't be zero.
+    Opcode = (Opcode << 8) | Byte;
+  } while (parseOptionalToken(AsmToken::Comma));
+  getTargetStreamer().emitARMWinCFICustom(Opcode);
+  return false;
+}
+
 /// Force static initialization.
 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeARMAsmParser() {
   RegisterMCAsmParser<ARMAsmParser> X(getTheARMLETarget());

diff  --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
index 16bc0ca179a7c..4bdaff359646e 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
@@ -101,6 +101,17 @@ class ARMTargetAsmStreamer : public ARMTargetStreamer {
   void AnnotateTLSDescriptorSequence(const MCSymbolRefExpr *SRE) override;
   void emitThumbSet(MCSymbol *Symbol, const MCExpr *Value) override;
 
+  void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
+  void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
+  void emitARMWinCFISaveSP(unsigned Reg) override;
+  void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
+  void emitARMWinCFISaveLR(unsigned Offset) override;
+  void emitARMWinCFIPrologEnd(bool Fragment) override;
+  void emitARMWinCFINop(bool Wide) override;
+  void emitARMWinCFIEpilogStart(unsigned Condition) override;
+  void emitARMWinCFIEpilogEnd() override;
+  void emitARMWinCFICustom(unsigned Opcode) override;
+
 public:
   ARMTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS,
                        MCInstPrinter &InstPrinter, bool VerboseAsm);
@@ -269,6 +280,101 @@ void ARMTargetAsmStreamer::emitUnwindRaw(int64_t Offset,
   OS << '\n';
 }
 
+void ARMTargetAsmStreamer::emitARMWinCFIAllocStack(unsigned Size, bool Wide) {
+  if (Wide)
+    OS << "\t.seh_stackalloc_w\t" << Size << "\n";
+  else
+    OS << "\t.seh_stackalloc\t" << Size << "\n";
+}
+
+static void printRegs(formatted_raw_ostream &OS, ListSeparator &LS, int First,
+                      int Last) {
+  if (First != Last)
+    OS << LS << "r" << First << "-r" << Last;
+  else
+    OS << LS << "r" << First;
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) {
+  if (Wide)
+    OS << "\t.seh_save_regs_w\t";
+  else
+    OS << "\t.seh_save_regs\t";
+  ListSeparator LS;
+  int First = -1;
+  OS << "{";
+  for (int I = 0; I <= 12; I++) {
+    if (Mask & (1 << I)) {
+      if (First < 0)
+        First = I;
+    } else {
+      if (First >= 0) {
+        printRegs(OS, LS, First, I - 1);
+        First = -1;
+      }
+    }
+  }
+  if (First >= 0)
+    printRegs(OS, LS, First, 12);
+  if (Mask & (1 << 14))
+    OS << LS << "lr";
+  OS << "}\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFISaveSP(unsigned Reg) {
+  OS << "\t.seh_save_sp\tr" << Reg << "\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFISaveFRegs(unsigned First,
+                                                  unsigned Last) {
+  if (First != Last)
+    OS << "\t.seh_save_fregs\t{d" << First << "-d" << Last << "}\n";
+  else
+    OS << "\t.seh_save_fregs\t{d" << First << "}\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFISaveLR(unsigned Offset) {
+  OS << "\t.seh_save_lr\t" << Offset << "\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
+  if (Fragment)
+    OS << "\t.seh_endprologue_fragment\n";
+  else
+    OS << "\t.seh_endprologue\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFINop(bool Wide) {
+  if (Wide)
+    OS << "\t.seh_nop_w\n";
+  else
+    OS << "\t.seh_nop\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
+  if (Condition == ARMCC::AL)
+    OS << "\t.seh_startepilogue\n";
+  else
+    OS << "\t.seh_startepilogue_cond\t"
+       << ARMCondCodeToString(static_cast<ARMCC::CondCodes>(Condition)) << "\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFIEpilogEnd() {
+  OS << "\t.seh_endepilogue\n";
+}
+
+void ARMTargetAsmStreamer::emitARMWinCFICustom(unsigned Opcode) {
+  int I;
+  for (I = 3; I > 0; I--)
+    if (Opcode & (0xffu << (8 * I)))
+      break;
+  ListSeparator LS;
+  OS << "\t.seh_custom\t";
+  for (; I >= 0; I--)
+    OS << LS << ((Opcode >> (8 * I)) & 0xff);
+  OS << "\n";
+}
+
 class ARMTargetELFStreamer : public ARMTargetStreamer {
 private:
   StringRef CurrentVendor;
@@ -1369,12 +1475,8 @@ MCTargetStreamer *createARMNullTargetStreamer(MCStreamer &S) {
   return new ARMTargetStreamer(S);
 }
 
-MCTargetStreamer *createARMObjectTargetStreamer(MCStreamer &S,
-                                                const MCSubtargetInfo &STI) {
-  const Triple &TT = STI.getTargetTriple();
-  if (TT.isOSBinFormatELF())
-    return new ARMTargetELFStreamer(S);
-  return new ARMTargetStreamer(S);
+MCTargetStreamer *createARMObjectTargetELFStreamer(MCStreamer &S) {
+  return new ARMTargetELFStreamer(S);
 }
 
 MCELFStreamer *createARMELFStreamer(MCContext &Context,

diff  --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp
index 77c0e3522911a..a7d50cb303557 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCAsmInfo.cpp
@@ -89,6 +89,7 @@ ARMCOFFMCAsmInfoMicrosoft::ARMCOFFMCAsmInfoMicrosoft() {
   AlignmentIsInBytes = false;
   SupportsDebugInformation = true;
   ExceptionsType = ExceptionHandling::WinEH;
+  WinEHEncodingType = WinEH::EncodingType::Itanium;
   PrivateGlobalPrefix = "$M";
   PrivateLabelPrefix = "$M";
   CommentString = "@";

diff  --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h
index 611e4e37055a9..e0c992f4fae23 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h
@@ -71,6 +71,8 @@ MCTargetStreamer *createARMTargetAsmStreamer(MCStreamer &S,
                                              bool isVerboseAsm);
 MCTargetStreamer *createARMObjectTargetStreamer(MCStreamer &S,
                                                 const MCSubtargetInfo &STI);
+MCTargetStreamer *createARMObjectTargetELFStreamer(MCStreamer &S);
+MCTargetStreamer *createARMObjectTargetWinCOFFStreamer(MCStreamer &S);
 
 MCCodeEmitter *createARMLEMCCodeEmitter(const MCInstrInfo &MCII,
                                         MCContext &Ctx);

diff  --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
index 4a22b82bd09b7..368829f2bb9f0 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMTargetStreamer.cpp
@@ -118,6 +118,17 @@ void
 ARMTargetStreamer::AnnotateTLSDescriptorSequence(const MCSymbolRefExpr *SRE) {}
 void ARMTargetStreamer::emitThumbSet(MCSymbol *Symbol, const MCExpr *Value) {}
 
+void ARMTargetStreamer::emitARMWinCFIAllocStack(unsigned Size, bool Wide) {}
+void ARMTargetStreamer::emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) {}
+void ARMTargetStreamer::emitARMWinCFISaveSP(unsigned Reg) {}
+void ARMTargetStreamer::emitARMWinCFISaveFRegs(unsigned First, unsigned Last) {}
+void ARMTargetStreamer::emitARMWinCFISaveLR(unsigned Offset) {}
+void ARMTargetStreamer::emitARMWinCFINop(bool Wide) {}
+void ARMTargetStreamer::emitARMWinCFIPrologEnd(bool Fragment) {}
+void ARMTargetStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {}
+void ARMTargetStreamer::emitARMWinCFIEpilogEnd() {}
+void ARMTargetStreamer::emitARMWinCFICustom(unsigned Opcode) {}
+
 static ARMBuildAttrs::CPUArch getArchForCPU(const MCSubtargetInfo &STI) {
   if (STI.getCPU() == "xscale")
     return ARMBuildAttrs::v5TEJ;
@@ -307,3 +318,13 @@ void ARMTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI) {
     emitAttribute(ARMBuildAttrs::BTI_extension, ARMBuildAttrs::AllowBTI);
   }
 }
+
+MCTargetStreamer *
+llvm::createARMObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
+  const Triple &TT = STI.getTargetTriple();
+  if (TT.isOSBinFormatELF())
+    return createARMObjectTargetELFStreamer(S);
+  if (TT.isOSBinFormatCOFF())
+    return createARMObjectTargetWinCOFFStreamer(S);
+  return new ARMTargetStreamer(S);
+}

diff  --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp
index 68a2ca8546c3f..1498b0975a9a6 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp
@@ -7,32 +7,60 @@
 //===----------------------------------------------------------------------===//
 
 #include "ARMMCTargetDesc.h"
+#include "Utils/ARMBaseInfo.h"
 #include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCWin64EH.h"
 #include "llvm/MC/MCWinCOFFStreamer.h"
 
 using namespace llvm;
 
 namespace {
 class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
+  Win64EH::ARMUnwindEmitter EHStreamer;
+
 public:
   ARMWinCOFFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> AB,
                      std::unique_ptr<MCCodeEmitter> CE,
                      std::unique_ptr<MCObjectWriter> OW)
       : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {}
 
+  void emitWinEHHandlerData(SMLoc Loc) override;
+  void emitWindowsUnwindTables() override;
+  void emitWindowsUnwindTables(WinEH::FrameInfo *Frame) override;
+
   void emitThumbFunc(MCSymbol *Symbol) override;
   void finishImpl() override;
 };
 
+void ARMWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) {
+  MCStreamer::emitWinEHHandlerData(Loc);
+
+  // We have to emit the unwind info now, because this directive
+  // actually switches to the .xdata section!
+  EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo(),
+                            /* HandlerData = */ true);
+}
+
+void ARMWinCOFFStreamer::emitWindowsUnwindTables(WinEH::FrameInfo *Frame) {
+  EHStreamer.EmitUnwindInfo(*this, Frame, /* HandlerData = */ false);
+}
+
+void ARMWinCOFFStreamer::emitWindowsUnwindTables() {
+  if (!getNumWinFrameInfos())
+    return;
+  EHStreamer.Emit(*this);
+}
+
 void ARMWinCOFFStreamer::emitThumbFunc(MCSymbol *Symbol) {
   getAssembler().setIsThumbFunc(Symbol);
 }
 
 void ARMWinCOFFStreamer::finishImpl() {
   emitFrames(nullptr);
+  emitWindowsUnwindTables();
 
   MCWinCOFFStreamer::finishImpl();
 }
@@ -49,3 +77,193 @@ MCStreamer *llvm::createARMWinCOFFStreamer(
   return S;
 }
 
+namespace {
+class ARMTargetWinCOFFStreamer : public llvm::ARMTargetStreamer {
+private:
+  // True if we are processing SEH directives in an epilogue.
+  bool InEpilogCFI = false;
+
+  // Symbol of the current epilog for which we are processing SEH directives.
+  MCSymbol *CurrentEpilog = nullptr;
+
+public:
+  ARMTargetWinCOFFStreamer(llvm::MCStreamer &S) : ARMTargetStreamer(S) {}
+
+  // The unwind codes on ARM Windows are documented at
+  // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
+  void emitARMWinCFIAllocStack(unsigned Size, bool Wide) override;
+  void emitARMWinCFISaveRegMask(unsigned Mask, bool Wide) override;
+  void emitARMWinCFISaveSP(unsigned Reg) override;
+  void emitARMWinCFISaveFRegs(unsigned First, unsigned Last) override;
+  void emitARMWinCFISaveLR(unsigned Offset) override;
+  void emitARMWinCFIPrologEnd(bool Fragment) override;
+  void emitARMWinCFINop(bool Wide) override;
+  void emitARMWinCFIEpilogStart(unsigned Condition) override;
+  void emitARMWinCFIEpilogEnd() override;
+  void emitARMWinCFICustom(unsigned Opcode) override;
+
+private:
+  void emitARMWinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
+};
+
+// Helper function to common out unwind code setup for those codes that can
+// belong to both prolog and epilog.
+void ARMTargetWinCOFFStreamer::emitARMWinUnwindCode(unsigned UnwindCode,
+                                                    int Reg, int Offset) {
+  auto &S = getStreamer();
+  WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
+  if (!CurFrame)
+    return;
+  MCSymbol *Label = S.emitCFILabel();
+  auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset);
+  if (InEpilogCFI)
+    CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
+  else
+    CurFrame->Instructions.push_back(Inst);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFIAllocStack(unsigned Size,
+                                                       bool Wide) {
+  unsigned Op = Win64EH::UOP_AllocSmall;
+  if (!Wide) {
+    if (Size / 4 > 0xffff)
+      Op = Win64EH::UOP_AllocHuge;
+    else if (Size / 4 > 0x7f)
+      Op = Win64EH::UOP_AllocLarge;
+  } else {
+    Op = Win64EH::UOP_WideAllocMedium;
+    if (Size / 4 > 0xffff)
+      Op = Win64EH::UOP_WideAllocHuge;
+    else if (Size / 4 > 0x3ff)
+      Op = Win64EH::UOP_WideAllocLarge;
+  }
+  emitARMWinUnwindCode(Op, -1, Size);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFISaveRegMask(unsigned Mask,
+                                                        bool Wide) {
+  assert(Mask != 0);
+  int Lr = (Mask & 0x4000) ? 1 : 0;
+  Mask &= ~0x4000;
+  if (Wide)
+    assert((Mask & ~0x1fff) == 0);
+  else
+    assert((Mask & ~0x00ff) == 0);
+  if (Mask && ((Mask + (1 << 4)) & Mask) == 0) {
+    if (Wide && (Mask & 0x1000) == 0 && (Mask & 0xff) == 0xf0) {
+      // One continuous range from r4 to r8-r11
+      for (int I = 11; I >= 8; I--) {
+        if (Mask & (1 << I)) {
+          emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegsR4R11LR, I, Lr);
+          return;
+        }
+      }
+      // If it actually was from r4 to r4-r7, continue below.
+    } else if (!Wide) {
+      // One continuous range from r4 to r4-r7
+      for (int I = 7; I >= 4; I--) {
+        if (Mask & (1 << I)) {
+          emitARMWinUnwindCode(Win64EH::UOP_SaveRegsR4R7LR, I, Lr);
+          return;
+        }
+      }
+      llvm_unreachable("logic error");
+    }
+  }
+  Mask |= Lr << 14;
+  if (Wide)
+    emitARMWinUnwindCode(Win64EH::UOP_WideSaveRegMask, Mask, 0);
+  else
+    emitARMWinUnwindCode(Win64EH::UOP_SaveRegMask, Mask, 0);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFISaveSP(unsigned Reg) {
+  emitARMWinUnwindCode(Win64EH::UOP_SaveSP, Reg, 0);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFISaveFRegs(unsigned First,
+                                                      unsigned Last) {
+  assert(First <= Last);
+  assert(First >= 16 || Last < 16);
+  assert(First <= 31 && Last <= 31);
+  if (First == 8)
+    emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD8D15, Last, 0);
+  else if (First <= 15)
+    emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD0D15, First, Last);
+  else
+    emitARMWinUnwindCode(Win64EH::UOP_SaveFRegD16D31, First, Last);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFISaveLR(unsigned Offset) {
+  emitARMWinUnwindCode(Win64EH::UOP_SaveLR, 0, Offset);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFINop(bool Wide) {
+  if (Wide)
+    emitARMWinUnwindCode(Win64EH::UOP_WideNop, -1, 0);
+  else
+    emitARMWinUnwindCode(Win64EH::UOP_Nop, -1, 0);
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFIPrologEnd(bool Fragment) {
+  auto &S = getStreamer();
+  WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
+  if (!CurFrame)
+    return;
+
+  MCSymbol *Label = S.emitCFILabel();
+  CurFrame->PrologEnd = Label;
+  WinEH::Instruction Inst =
+      WinEH::Instruction(Win64EH::UOP_End, /*Label=*/nullptr, -1, 0);
+  auto it = CurFrame->Instructions.begin();
+  CurFrame->Instructions.insert(it, Inst);
+  CurFrame->Fragment = Fragment;
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogStart(unsigned Condition) {
+  auto &S = getStreamer();
+  WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
+  if (!CurFrame)
+    return;
+
+  InEpilogCFI = true;
+  CurrentEpilog = S.emitCFILabel();
+  CurFrame->EpilogMap[CurrentEpilog].Condition = Condition;
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFIEpilogEnd() {
+  auto &S = getStreamer();
+  WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc());
+  if (!CurFrame)
+    return;
+
+  std::vector<WinEH::Instruction> &Epilog =
+      CurFrame->EpilogMap[CurrentEpilog].Instructions;
+
+  unsigned UnwindCode = Win64EH::UOP_End;
+  if (!Epilog.empty()) {
+    WinEH::Instruction EndInstr = Epilog.back();
+    if (EndInstr.Operation == Win64EH::UOP_Nop) {
+      UnwindCode = Win64EH::UOP_EndNop;
+      Epilog.pop_back();
+    } else if (EndInstr.Operation == Win64EH::UOP_WideNop) {
+      UnwindCode = Win64EH::UOP_WideEndNop;
+      Epilog.pop_back();
+    }
+  }
+
+  InEpilogCFI = false;
+  WinEH::Instruction Inst = WinEH::Instruction(UnwindCode, nullptr, -1, 0);
+  CurFrame->EpilogMap[CurrentEpilog].Instructions.push_back(Inst);
+  CurrentEpilog = nullptr;
+}
+
+void ARMTargetWinCOFFStreamer::emitARMWinCFICustom(unsigned Opcode) {
+  emitARMWinUnwindCode(Win64EH::UOP_Custom, 0, Opcode);
+}
+
+} // end anonymous namespace
+
+MCTargetStreamer *llvm::createARMObjectTargetWinCOFFStreamer(MCStreamer &S) {
+  return new ARMTargetWinCOFFStreamer(S);
+}

diff  --git a/llvm/test/MC/ARM/seh.s b/llvm/test/MC/ARM/seh.s
new file mode 100644
index 0000000000000..1b92c0d14e3b3
--- /dev/null
+++ b/llvm/test/MC/ARM/seh.s
@@ -0,0 +1,347 @@
+// This test checks that the SEH directives emit the correct unwind data.
+
+// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=obj %s | llvm-readobj -S -r -u - | FileCheck %s
+
+// Check that the output assembler directives also can be parsed, and
+// that they produce equivalent output:
+
+// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=asm %s | llvm-mc -triple thumbv7-pc-win32 -filetype=obj - | llvm-readobj -S -r -u - | FileCheck %s
+
+// CHECK:      Sections [
+// CHECK:        Section {
+// CHECK:          Name: .text
+// CHECK:          RelocationCount: 1
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_CODE
+// CHECK-NEXT:       MEM_16BIT
+// CHECK-NEXT:       MEM_EXECUTE
+// CHECK-NEXT:       MEM_PURGEABLE
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK:        Section {
+// CHECK:          Name: .xdata
+// CHECK:          RawDataSize: 120
+// CHECK:          RelocationCount: 1
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_INITIALIZED_DATA
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK:        Section {
+// CHECK:          Name: .pdata
+// CHECK:          RelocationCount: 10
+// CHECK:          Characteristics [
+// CHECK-NEXT:       ALIGN_4BYTES
+// CHECK-NEXT:       CNT_INITIALIZED_DATA
+// CHECK-NEXT:       MEM_READ
+// CHECK-NEXT:     ]
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+// CHECK-NEXT: Relocations [
+// CHECK-NEXT:   Section (1) .text {
+// CHECK-NEXT:     0x5C IMAGE_REL_ARM_BRANCH24T tailcall
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section (4) .xdata {
+// CHECK-NEXT:     0x38 IMAGE_REL_ARM_ADDR32NB __C_specific_handler
+// CHECK-NEXT:   }
+// CHECK-NEXT:   Section (5) .pdata {
+// CHECK-NEXT:     0x0 IMAGE_REL_ARM_ADDR32NB .text
+// CHECK-NEXT:     0x4 IMAGE_REL_ARM_ADDR32NB .xdata
+// CHECK-NEXT:     0x8 IMAGE_REL_ARM_ADDR32NB .text
+// CHECK-NEXT:     0xC IMAGE_REL_ARM_ADDR32NB .xdata
+// CHECK-NEXT:     0x10 IMAGE_REL_ARM_ADDR32NB .text
+// CHECK-NEXT:     0x14 IMAGE_REL_ARM_ADDR32NB .xdata
+// CHECK-NEXT:     0x18 IMAGE_REL_ARM_ADDR32NB .text
+// CHECK-NEXT:     0x1C IMAGE_REL_ARM_ADDR32NB .xdata
+// CHECK-NEXT:     0x20 IMAGE_REL_ARM_ADDR32NB .text
+// CHECK-NEXT:     0x24 IMAGE_REL_ARM_ADDR32NB .xdata
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+// CHECK-NEXT: UnwindInformation [
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: func
+// CHECK-NEXT:     ExceptionRecord: .xdata
+// CHECK-NEXT:     ExceptionData {
+// CHECK-NEXT:       FunctionLength: 86
+// CHECK:            Fragment: No
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xed 0xf8           ; push {r3-r7, lr}
+// CHECK-NEXT:         0xf6 0x27           ; vpush {d18-d23}
+// CHECK-NEXT:         0xf5 0x7e           ; vpush {d7-d14}
+// CHECK-NEXT:         0xfb                ; nop
+// CHECK-NEXT:         0xce                ; mov r14, sp
+// CHECK-NEXT:         0xe3                ; vpush {d8-d11}
+// CHECK-NEXT:         0xe6                ; vpush {d8-d14}
+// CHECK-NEXT:         0xed 0xf8           ; push {r3-r7, lr}
+// CHECK-NEXT:         0xbd 0x50           ; push.w {r4, r6, r8, r10-r12, lr}
+// CHECK-NEXT:         0xd7                ; push {r4-r7, lr}
+// CHECK-NEXT:         0xdd                ; push.w {r4-r9, lr}
+// CHECK-NEXT:         0xfa 0x01 0x00 0x00 ; sub.w sp, sp, #(65536 * 4)
+// CHECK-NEXT:         0xfc                ; nop.w
+// CHECK-NEXT:         0xfc                ; nop.w
+// CHECK-NEXT:         0xf9 0x04 0x00      ; sub.w sp, sp, #(1024 * 4)
+// CHECK-NEXT:         0xe8 0x80           ; sub.w sp, #(128 * 4)
+// CHECK-NEXT:         0xe8 0x80           ; sub.w sp, #(128 * 4)
+// CHECK-NEXT:         0x06                ; sub sp, #(6 * 4)
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK-NEXT:           StartOffset: 31
+// CHECK-NEXT:           Condition: 14
+// CHECK-NEXT:           EpilogueStartIndex: 31
+// CHECK-NEXT:           Opcodes [
+// CHECK-NEXT:             0xfc                ; nop.w
+// CHECK-NEXT:             0xf7 0x00 0x80      ; add sp, sp, #(128 * 4)
+// CHECK-NEXT:             0xfc                ; nop.w
+// CHECK-NEXT:             0xfc                ; nop.w
+// CHECK-NEXT:             0xf8 0x01 0x00 0x00 ; add sp, sp, #(65536 * 4)
+// CHECK-NEXT:             0x06                ; add sp, #(6 * 4)
+// CHECK-NEXT:             0xef 0x04           ; ldr.w lr, [sp], #16
+// CHECK-NEXT:             0xfd                ; bx <reg>
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       ExceptionHandler [
+// CHECK-NEXT:         Routine: __C_specific_handler
+// CHECK-NEXT:         Parameter: 0x0
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: func2
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xd3                ; push {r4-r7}
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK:                Opcodes [
+// CHECK-NEXT:             0xd2                ; pop {r4-r6}
+// CHECK-NEXT:             0xfe                ; b.w <target>
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: func3
+// CHECK:            FunctionLength: 8
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xd5                ; push {r4-r5, lr}
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK-NEXT:           StartOffset: 3
+// CHECK-NEXT:           Condition: 14
+// CHECK-NEXT:           EpilogueStartIndex: 2
+// CHECK-NEXT:           Opcodes [
+// CHECK-NEXT:             0xd6                ; pop {r4-r6, pc}
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: fragment
+// CHECK:            FunctionLength: 6
+// CHECK:            Fragment: Yes
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xcb                ; mov r11, sp
+// CHECK-NEXT:         0x10                ; sub sp, #(16 * 4)
+// CHECK-NEXT:         0xd5                ; push {r4-r5, lr}
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK-NEXT:           StartOffset: 1
+// CHECK-NEXT:           Condition: 14
+// CHECK-NEXT:           EpilogueStartIndex: 4
+// CHECK-NEXT:           Opcodes [
+// CHECK-NEXT:             0x10                ; add sp, #(16 * 4)
+// CHECK-NEXT:             0xd5                ; pop {r4-r5, pc}
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT:   RuntimeFunction {
+// CHECK-NEXT:     Function: condepilog
+// CHECK:            FunctionLength: 8
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xd5                ; push {r4-r5, lr}
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK-NEXT:           StartOffset: 3
+// CHECK-NEXT:           Condition: 10
+// CHECK-NEXT:           EpilogueStartIndex: 2
+// CHECK-NEXT:           Opcodes [
+// CHECK-NEXT:             0xd5                ; pop {r4-r5, pc}
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
+        .text
+        .syntax unified
+        .globl func
+        .def func
+        .scl 2
+        .type 32
+        .endef
+        .seh_proc func
+func:
+        sub sp, sp, #24
+        .seh_stackalloc 24
+        sub sp, sp, #512
+        .seh_stackalloc_w 512
+        sub sp, sp, #512
+        .seh_stackalloc_w 512
+        sub sp, sp, #4096
+        .seh_stackalloc_w 4096
+        movw r7, #0
+        .seh_nop_w
+        movt r7, #0x4 // 0x40000
+        .seh_nop_w
+        sub sp, sp, r7
+        .seh_stackalloc_w 0x40000
+        push {r4-r8,lr}
+        .seh_save_regs_w {r4-r9,lr}
+        push {r4-r7,lr}
+        .seh_save_regs {r4-r7,lr}
+        push {r4,r6,r8,r10,r11,r12,lr}
+        .seh_save_regs_w {r4,r6,r8,r10,r11,r12,lr}
+        push {r3-r7,lr}
+        .seh_save_regs {r3-r7,lr}
+        vpush {d8-d14}
+        .seh_save_fregs {d8-d14}
+        vpush {q4-q5}
+        .seh_save_fregs {q4-q5}
+        mov lr, sp
+        .seh_save_sp lr
+        nop
+        .seh_nop
+        vpush {d7-d14}
+        .seh_save_fregs {d7-d14}
+        vpush {d18-d23}
+        .seh_save_fregs {d18-d23}
+        push {r3-r7,lr}
+        .seh_custom 0xed, 0xf8
+        .seh_endprologue
+        nop
+        .seh_startepilogue
+        mov r7, #512
+        .seh_nop_w
+        add sp, sp, r7
+        .seh_stackalloc 512
+        movw r7, #0
+        .seh_nop_w
+        movt r7, #0x4 // 0x40000
+        .seh_nop_w
+        add sp, sp, r7
+        .seh_stackalloc 0x40000
+        add sp, sp, #24
+        .seh_stackalloc 24
+        ldr lr, [sp], #16
+        .seh_save_lr 16
+        bx lr
+        .seh_nop
+        .seh_endepilogue
+        .seh_handler __C_specific_handler, %except
+        .seh_handlerdata
+        .long 0
+        .text
+        .seh_endproc
+
+        .seh_proc func2
+func2:
+        push {r4-r7}
+        .seh_save_regs {r4-r7}
+        .seh_endprologue
+        nop
+        .seh_startepilogue
+        pop {r4-r6}
+        .seh_save_regs {r4-r6}
+        b.w tailcall
+        .seh_nop_w
+        .seh_endepilogue
+        .seh_endproc
+
+        .seh_proc func3
+func3:
+        push {r4-r5,lr}
+        .seh_save_regs {r4-r5,lr}
+        .seh_endprologue
+        nop
+        // The p2align causes the length of the function to be unknown.
+        .p2align 1
+        nop
+        .seh_startepilogue
+        pop {r4-r6,pc}
+        .seh_save_regs {r4-r6,pc}
+        .seh_endepilogue
+        .seh_endproc
+
+        .seh_proc fragment
+fragment:
+        // Prologue opcodes without matching instructions
+        .seh_save_regs {r4-r5,lr}
+        .seh_stackalloc 64
+        .seh_save_sp r11
+        .seh_endprologue_fragment
+        nop
+        .seh_startepilogue
+        add sp, sp, #64
+        .seh_stackalloc 64
+        pop {r4-r5,pc}
+        .seh_save_regs {r4-r5,pc}
+        .seh_endepilogue
+        .seh_endproc
+
+        .seh_proc condepilog
+condepilog:
+        push {r4-r5,lr}
+        .seh_save_regs {r4-r5,lr}
+        .seh_endprologue
+        nop
+        it ge
+        .seh_startepilogue_cond ge
+        popge {r4-r5,pc}
+        .seh_save_regs {r4-r5,pc}
+        .seh_endepilogue
+        .seh_endproc
+
+        // Function with no .seh directives; no pdata/xdata entries are
+        // generated.
+        .globl smallFunc
+        .def smallFunc
+        .scl 2
+        .type 32
+        .endef
+        .seh_proc smallFunc
+smallFunc:
+        bx lr
+        .seh_endproc
+
+        // Function with no .seh directives, but with .seh_handlerdata.
+        // No xdata/pdata entries are generated, but the custom handler data
+        // (the .long after .seh_handlerdata) is left orphaned in the xdata
+        // section.
+        .globl handlerFunc
+        .def handlerFunc
+        .scl 2
+        .type 32
+        .endef
+        .seh_proc handlerFunc
+handlerFunc:
+        bx lr
+        .seh_handler __C_specific_handler, %except
+        .seh_handlerdata
+        .long 0
+        .text
+        .seh_endproc


        


More information about the llvm-commits mailing list