[llvm] 5b86d13 - [AArch64] Generate and parse SEH assembly directives

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 29 05:16:35 PDT 2020


Author: Martin Storsjö
Date: 2020-08-29T15:15:22+03:00
New Revision: 5b86d130e2baed7221b09087c506f5974fe65f22

URL: https://github.com/llvm/llvm-project/commit/5b86d130e2baed7221b09087c506f5974fe65f22
DIFF: https://github.com/llvm/llvm-project/commit/5b86d130e2baed7221b09087c506f5974fe65f22.diff

LOG: [AArch64] Generate and parse SEH assembly directives

This ensures that you get the same output regardless if generating
code directly to an object file or if generating assembly and
assembling that.

Add implementations of the EmitARM64WinCFI*() methods in
AArch64TargetAsmStreamer, and fill in one blank in MCAsmStreamer.

Add corresponding directive handlers in AArch64AsmParser and
COFFAsmParser.

Some SEH directive names have been picked to match the prior art
for SEH assembly directives for x86_64, e.g. the spelling of
".seh_startepilogue" matching the preexisting ".seh_endprologue".

For the directives for saving registers, the exact spelling
from the arm64 documentation is picked, e.g. ".seh_save_reg" (to follow
that naming for all the other ones, e.g. ".seh_save_fregp_x"), while
the corresponding one for x86_64 is plain ".seh_savereg" without the
second underscore.

Directives in the epilogues have the same names as in prologues,
e.g. .seh_savereg, even though the registers are restored, not
saved, at that point.

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

Added: 
    

Modified: 
    llvm/lib/MC/MCAsmStreamer.cpp
    llvm/lib/MC/MCParser/COFFAsmParser.cpp
    llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
    llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
    llvm/test/CodeGen/AArch64/arm64-windows-calls.ll
    llvm/test/CodeGen/AArch64/lrint-conv-fp16-win.ll
    llvm/test/CodeGen/AArch64/lrint-conv-win.ll
    llvm/test/CodeGen/AArch64/lround-conv-fp16-win.ll
    llvm/test/CodeGen/AArch64/lround-conv-win.ll
    llvm/test/CodeGen/AArch64/powi-windows.ll
    llvm/test/CodeGen/AArch64/seh_funclet_x1.ll
    llvm/test/CodeGen/AArch64/win64-jumptable.ll
    llvm/test/CodeGen/AArch64/win_cst_pool.ll
    llvm/test/CodeGen/AArch64/windows-extern-weak.ll
    llvm/test/CodeGen/AArch64/wineh-try-catch-cbz.ll
    llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
    llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
    llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
    llvm/test/CodeGen/AArch64/wineh1.mir
    llvm/test/MC/AArch64/seh.s

Removed: 
    


################################################################################
diff  --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 490557a2db08..647197d8de4d 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -1822,8 +1822,11 @@ void MCAsmStreamer::EmitWinCFIEndProc(SMLoc Loc) {
   EmitEOL();
 }
 
-// TODO: Implement
 void MCAsmStreamer::EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) {
+  MCStreamer::EmitWinCFIFuncletOrFuncEnd(Loc);
+
+  OS << "\t.seh_endfunclet";
+  EmitEOL();
 }
 
 void MCAsmStreamer::EmitWinCFIStartChained(SMLoc Loc) {

diff  --git a/llvm/lib/MC/MCParser/COFFAsmParser.cpp b/llvm/lib/MC/MCParser/COFFAsmParser.cpp
index 2104fb83b309..1d966ff8632a 100644
--- a/llvm/lib/MC/MCParser/COFFAsmParser.cpp
+++ b/llvm/lib/MC/MCParser/COFFAsmParser.cpp
@@ -77,6 +77,8 @@ class COFFAsmParser : public MCAsmParserExtension {
                                                                    ".seh_proc");
     addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndProc>(
                                                                 ".seh_endproc");
+    addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndFuncletOrFunc>(
+                                                                ".seh_endfunclet");
     addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveStartChained>(
                                                            ".seh_startchained");
     addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndChained>(
@@ -131,6 +133,7 @@ class COFFAsmParser : public MCAsmParserExtension {
   // Win64 EH directives.
   bool ParseSEHDirectiveStartProc(StringRef, SMLoc);
   bool ParseSEHDirectiveEndProc(StringRef, SMLoc);
+  bool ParseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc);
   bool ParseSEHDirectiveStartChained(StringRef, SMLoc);
   bool ParseSEHDirectiveEndChained(StringRef, SMLoc);
   bool ParseSEHDirectiveHandler(StringRef, SMLoc);
@@ -629,6 +632,12 @@ bool COFFAsmParser::ParseSEHDirectiveEndProc(StringRef, SMLoc Loc) {
   return false;
 }
 
+bool COFFAsmParser::ParseSEHDirectiveEndFuncletOrFunc(StringRef, SMLoc Loc) {
+  Lex();
+  getStreamer().EmitWinCFIFuncletOrFuncEnd(Loc);
+  return false;
+}
+
 bool COFFAsmParser::ParseSEHDirectiveStartChained(StringRef, SMLoc Loc) {
   Lex();
   getStreamer().EmitWinCFIStartChained(Loc);

diff  --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 539d1e1c6452..a74d15de2556 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MCTargetDesc/AArch64AddressingModes.h"
+#include "MCTargetDesc/AArch64InstPrinter.h"
 #include "MCTargetDesc/AArch64MCExpr.h"
 #include "MCTargetDesc/AArch64MCTargetDesc.h"
 #include "MCTargetDesc/AArch64TargetStreamer.h"
@@ -160,6 +161,10 @@ class AArch64AsmParser : public MCTargetAsmParser {
   bool parseOptionalMulOperand(OperandVector &Operands);
   bool parseOperand(OperandVector &Operands, bool isCondCode,
                     bool invertCondCode);
+  bool parseImmExpr(int64_t &Out);
+  bool parseComma();
+  bool parseRegisterInRange(unsigned &Out, unsigned Base, unsigned First,
+                            unsigned Last);
 
   bool showMatchError(SMLoc Loc, unsigned ErrCode, uint64_t ErrorInfo,
                       OperandVector &Operands);
@@ -179,6 +184,24 @@ class AArch64AsmParser : public MCTargetAsmParser {
   bool parseDirectiveCFINegateRAState();
   bool parseDirectiveCFIBKeyFrame();
 
+  bool parseDirectiveSEHAllocStack(SMLoc L);
+  bool parseDirectiveSEHPrologEnd(SMLoc L);
+  bool parseDirectiveSEHSaveFPLR(SMLoc L);
+  bool parseDirectiveSEHSaveFPLRX(SMLoc L);
+  bool parseDirectiveSEHSaveReg(SMLoc L);
+  bool parseDirectiveSEHSaveRegX(SMLoc L);
+  bool parseDirectiveSEHSaveRegP(SMLoc L);
+  bool parseDirectiveSEHSaveRegPX(SMLoc L);
+  bool parseDirectiveSEHSaveFReg(SMLoc L);
+  bool parseDirectiveSEHSaveFRegX(SMLoc L);
+  bool parseDirectiveSEHSaveFRegP(SMLoc L);
+  bool parseDirectiveSEHSaveFRegPX(SMLoc L);
+  bool parseDirectiveSEHSetFP(SMLoc L);
+  bool parseDirectiveSEHAddFP(SMLoc L);
+  bool parseDirectiveSEHNop(SMLoc L);
+  bool parseDirectiveSEHEpilogStart(SMLoc L);
+  bool parseDirectiveSEHEpilogEnd(SMLoc L);
+
   bool validateInstruction(MCInst &Inst, SMLoc &IDLoc,
                            SmallVectorImpl<SMLoc> &Loc);
   bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
@@ -3741,6 +3764,66 @@ bool AArch64AsmParser::parseOperand(OperandVector &Operands, bool isCondCode,
   }
 }
 
+bool AArch64AsmParser::parseImmExpr(int64_t &Out) {
+  const MCExpr *Expr = nullptr;
+  SMLoc L = 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;
+}
+
+bool AArch64AsmParser::parseComma() {
+  if (check(getParser().getTok().isNot(AsmToken::Comma), getLoc(),
+            "expected comma"))
+    return true;
+  // Eat the comma
+  getParser().Lex();
+  return false;
+}
+
+bool AArch64AsmParser::parseRegisterInRange(unsigned &Out, unsigned Base,
+                                            unsigned First, unsigned Last) {
+  unsigned Reg;
+  SMLoc Start, End;
+  if (check(ParseRegister(Reg, Start, End), getLoc(), "expected register"))
+    return true;
+
+  // Special handling for FP and LR; they aren't linearly after x28 in
+  // the registers enum.
+  unsigned RangeEnd = Last;
+  if (Base == AArch64::X0) {
+    if (Last == AArch64::FP) {
+      RangeEnd = AArch64::X28;
+      if (Reg == AArch64::FP) {
+        Out = 29;
+        return false;
+      }
+    }
+    if (Last == AArch64::LR) {
+      RangeEnd = AArch64::X28;
+      if (Reg == AArch64::FP) {
+        Out = 29;
+        return false;
+      } else if (Reg == AArch64::LR) {
+        Out = 30;
+        return false;
+      }
+    }
+  }
+
+  if (check(Reg < First || Reg > RangeEnd, Start,
+            Twine("expected register in range ") +
+                AArch64InstPrinter::getRegisterName(First) + " to " +
+                AArch64InstPrinter::getRegisterName(Last)))
+    return true;
+  Out = Reg - Base;
+  return false;
+}
+
 bool AArch64AsmParser::regsEqual(const MCParsedAsmOperand &Op1,
                                  const MCParsedAsmOperand &Op2) const {
   auto &AOp1 = static_cast<const AArch64Operand&>(Op1);
@@ -5059,6 +5142,7 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
   const MCObjectFileInfo::Environment Format =
     getContext().getObjectFileInfo()->getObjectFileType();
   bool IsMachO = Format == MCObjectFileInfo::IsMachO;
+  bool IsCOFF = Format == MCObjectFileInfo::IsCOFF;
 
   auto IDVal = DirectiveID.getIdentifier().lower();
   SMLoc Loc = DirectiveID.getLoc();
@@ -5085,6 +5169,43 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
       parseDirectiveLOH(IDVal, Loc);
     else
       return true;
+  } else if (IsCOFF) {
+    if (IDVal == ".seh_stackalloc")
+      parseDirectiveSEHAllocStack(Loc);
+    else if (IDVal == ".seh_endprologue")
+      parseDirectiveSEHPrologEnd(Loc);
+    else if (IDVal == ".seh_save_fplr")
+      parseDirectiveSEHSaveFPLR(Loc);
+    else if (IDVal == ".seh_save_fplr_x")
+      parseDirectiveSEHSaveFPLRX(Loc);
+    else if (IDVal == ".seh_save_reg")
+      parseDirectiveSEHSaveReg(Loc);
+    else if (IDVal == ".seh_save_reg_x")
+      parseDirectiveSEHSaveRegX(Loc);
+    else if (IDVal == ".seh_save_regp")
+      parseDirectiveSEHSaveRegP(Loc);
+    else if (IDVal == ".seh_save_regp_x")
+      parseDirectiveSEHSaveRegPX(Loc);
+    else if (IDVal == ".seh_save_freg")
+      parseDirectiveSEHSaveFReg(Loc);
+    else if (IDVal == ".seh_save_freg_x")
+      parseDirectiveSEHSaveFRegX(Loc);
+    else if (IDVal == ".seh_save_fregp")
+      parseDirectiveSEHSaveFRegP(Loc);
+    else if (IDVal == ".seh_save_fregp_x")
+      parseDirectiveSEHSaveFRegPX(Loc);
+    else if (IDVal == ".seh_set_fp")
+      parseDirectiveSEHSetFP(Loc);
+    else if (IDVal == ".seh_add_fp")
+      parseDirectiveSEHAddFP(Loc);
+    else if (IDVal == ".seh_nop")
+      parseDirectiveSEHNop(Loc);
+    else if (IDVal == ".seh_startepilogue")
+      parseDirectiveSEHEpilogStart(Loc);
+    else if (IDVal == ".seh_endepilogue")
+      parseDirectiveSEHEpilogEnd(Loc);
+    else
+      return true;
   } else
     return true;
   return false;
@@ -5507,6 +5628,177 @@ bool AArch64AsmParser::parseDirectiveCFIBKeyFrame() {
   return false;
 }
 
+/// parseDirectiveSEHAllocStack
+/// ::= .seh_stackalloc
+bool AArch64AsmParser::parseDirectiveSEHAllocStack(SMLoc L) {
+  int64_t Size;
+  if (parseImmExpr(Size))
+    return true;
+  getTargetStreamer().EmitARM64WinCFIAllocStack(Size);
+  return false;
+}
+
+/// parseDirectiveSEHPrologEnd
+/// ::= .seh_endprologue
+bool AArch64AsmParser::parseDirectiveSEHPrologEnd(SMLoc L) {
+  getTargetStreamer().EmitARM64WinCFIPrologEnd();
+  return false;
+}
+
+/// parseDirectiveSEHSaveFPLR
+/// ::= .seh_save_fplr
+bool AArch64AsmParser::parseDirectiveSEHSaveFPLR(SMLoc L) {
+  int64_t Offset;
+  if (parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFPLR(Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFPLRX
+/// ::= .seh_save_fplr_x
+bool AArch64AsmParser::parseDirectiveSEHSaveFPLRX(SMLoc L) {
+  int64_t Offset;
+  if (parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFPLRX(Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveReg
+/// ::= .seh_save_reg
+bool AArch64AsmParser::parseDirectiveSEHSaveReg(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X19, AArch64::LR) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveReg(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveRegX
+/// ::= .seh_save_reg_x
+bool AArch64AsmParser::parseDirectiveSEHSaveRegX(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X19, AArch64::LR) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveRegX(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveRegP
+/// ::= .seh_save_regp
+bool AArch64AsmParser::parseDirectiveSEHSaveRegP(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X19, AArch64::LR) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveRegP(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveRegPX
+/// ::= .seh_save_regp_x
+bool AArch64AsmParser::parseDirectiveSEHSaveRegPX(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X19, AArch64::X28) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveRegPX(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFReg
+/// ::= .seh_save_freg
+bool AArch64AsmParser::parseDirectiveSEHSaveFReg(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D8, AArch64::D15) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFReg(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFRegX
+/// ::= .seh_save_freg_x
+bool AArch64AsmParser::parseDirectiveSEHSaveFRegX(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D8, AArch64::D15) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFRegX(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFRegP
+/// ::= .seh_save_fregp
+bool AArch64AsmParser::parseDirectiveSEHSaveFRegP(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D8, AArch64::D15) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFRegP(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSaveFRegPX
+/// ::= .seh_save_fregp_x
+bool AArch64AsmParser::parseDirectiveSEHSaveFRegPX(SMLoc L) {
+  unsigned Reg;
+  int64_t Offset;
+  if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D8, AArch64::D15) ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+  getTargetStreamer().EmitARM64WinCFISaveFRegPX(Reg, Offset);
+  return false;
+}
+
+/// parseDirectiveSEHSetFP
+/// ::= .seh_set_fp
+bool AArch64AsmParser::parseDirectiveSEHSetFP(SMLoc L) {
+  getTargetStreamer().EmitARM64WinCFISetFP();
+  return false;
+}
+
+/// parseDirectiveSEHAddFP
+/// ::= .seh_add_fp
+bool AArch64AsmParser::parseDirectiveSEHAddFP(SMLoc L) {
+  int64_t Size;
+  if (parseImmExpr(Size))
+    return true;
+  getTargetStreamer().EmitARM64WinCFIAddFP(Size);
+  return false;
+}
+
+/// parseDirectiveSEHNop
+/// ::= .seh_nop
+bool AArch64AsmParser::parseDirectiveSEHNop(SMLoc L) {
+  getTargetStreamer().EmitARM64WinCFINop();
+  return false;
+}
+
+/// parseDirectiveSEHEpilogStart
+/// ::= .seh_startepilogue
+bool AArch64AsmParser::parseDirectiveSEHEpilogStart(SMLoc L) {
+  getTargetStreamer().EmitARM64WinCFIEpilogStart();
+  return false;
+}
+
+/// parseDirectiveSEHEpilogEnd
+/// ::= .seh_endepilogue
+bool AArch64AsmParser::parseDirectiveSEHEpilogEnd(SMLoc L) {
+  getTargetStreamer().EmitARM64WinCFIEpilogEnd();
+  return false;
+}
+
 bool
 AArch64AsmParser::classifySymbolRef(const MCExpr *Expr,
                                     AArch64MCExpr::VariantKind &ELFRefKind,

diff  --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
index fe4c34be1519..ce40e9681467 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
@@ -47,6 +47,48 @@ class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
 
   void emitInst(uint32_t Inst) override;
 
+  void EmitARM64WinCFIAllocStack(unsigned Size) override {
+    OS << "\t.seh_stackalloc " << Size << "\n";
+  }
+  void EmitARM64WinCFISaveFPLR(int Offset) override {
+    OS << "\t.seh_save_fplr " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveFPLRX(int Offset) override {
+    OS << "\t.seh_save_fplr_x " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_reg x" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_reg_x x" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_regp x" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_regp_x x" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_freg d" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_freg_x d" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_fregp d" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_fregp_x d" << Reg << ", " << Offset << "\n";
+  }
+  void EmitARM64WinCFISetFP() override { OS << "\t.seh_set_fp\n"; }
+  void EmitARM64WinCFIAddFP(unsigned Size) override {
+    OS << "\t.seh_add_fp " << Size << "\n";
+  }
+  void EmitARM64WinCFINop() override { OS << "\t.seh_nop\n"; }
+  void EmitARM64WinCFIPrologEnd() override { OS << "\t.seh_endprologue\n"; }
+  void EmitARM64WinCFIEpilogStart() override { OS << "\t.seh_startepilogue\n"; }
+  void EmitARM64WinCFIEpilogEnd() override { OS << "\t.seh_endepilogue\n"; }
+
 public:
   AArch64TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
 };

diff  --git a/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll b/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll
index 2a0793aa851d..13a688174832 100644
--- a/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll
+++ b/llvm/test/CodeGen/AArch64/arm64-windows-calls.ll
@@ -29,6 +29,7 @@ entry:
 ; CHECK:         stp     xzr, xzr, [sp, #-16]!
 ; CHECK-NEXT:    mov     x0, xzr
 ; CHECK-NEXT:    mov     x1, xzr
+; CHECK-NEXT:    .seh_startepilogue
 ; CHECK-NEXT:    add     sp, sp, #16
 
   %retval = alloca %struct.S2, align 4

diff  --git a/llvm/test/CodeGen/AArch64/lrint-conv-fp16-win.ll b/llvm/test/CodeGen/AArch64/lrint-conv-fp16-win.ll
index ec9a8b2be874..4299ce89ad18 100644
--- a/llvm/test/CodeGen/AArch64/lrint-conv-fp16-win.ll
+++ b/llvm/test/CodeGen/AArch64/lrint-conv-fp16-win.ll
@@ -3,6 +3,8 @@
 ; CHECK-LABEL: testmhhs:
 ; CHECK:       frintx  h0, h0
 ; CHECK-NEXT:  fcvtzs  w0, h0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i16 @testmhhs(half %x) {
 entry:
@@ -14,6 +16,8 @@ entry:
 ; CHECK-LABEL: testmhws:
 ; CHECK:       frintx  h0, h0
 ; CHECK-NEXT:  fcvtzs  w0, h0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i32 @testmhws(half %x) {
 entry:
@@ -25,6 +29,8 @@ entry:
 ; CHECK:       frintx  h0, h0
 ; CHECK-NEXT:  fcvtzs  w8, h0
 ; CHECK-NEXT:  sxtw    x0, w8
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmhxs(half %x) {
 entry:

diff  --git a/llvm/test/CodeGen/AArch64/lrint-conv-win.ll b/llvm/test/CodeGen/AArch64/lrint-conv-win.ll
index 490f009c3fba..8195ffe8a9fd 100644
--- a/llvm/test/CodeGen/AArch64/lrint-conv-win.ll
+++ b/llvm/test/CodeGen/AArch64/lrint-conv-win.ll
@@ -4,6 +4,8 @@
 ; CHECK:       frintx  [[SREG:s[0-9]+]], s0
 ; CHECK-NEXT:  fcvtzs  [[WREG:w[0-9]+]], [[SREG]]
 ; CHECK-NEXT:  sxtw    x0, [[WREG]]
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmsxs(float %x) {
 entry:
@@ -15,6 +17,8 @@ entry:
 ; CHECK-LABEL: testmsws:
 ; CHECK:       frintx  [[SREG:s[0-9]+]], s0
 ; CHECK-NEXT:  fcvtzs  [[WREG:w[0-9]+]], [[SREG]]
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i32 @testmsws(float %x) {
 entry:
@@ -26,6 +30,8 @@ entry:
 ; CHECK:       frintx  [[DREG:d[0-9]+]], d0
 ; CHECK-NEXT:  fcvtzs  [[WREG:w[0-9]+]], [[DREG]]
 ; CHECK-NEXT:  sxtw    x0, [[WREG]]
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmsxd(double %x) {
 entry:
@@ -37,6 +43,8 @@ entry:
 ; CHECK-LABEL: testmswd:
 ; CHECK:       frintx  [[DREG:d[0-9]+]], d0
 ; CHECK-NEXT:  fcvtzs  [[WREG:w[0-9]+]], [[DREG]]
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i32 @testmswd(double %x) {
 entry:

diff  --git a/llvm/test/CodeGen/AArch64/lround-conv-fp16-win.ll b/llvm/test/CodeGen/AArch64/lround-conv-fp16-win.ll
index 5eabc2a4f463..ea14659203ed 100644
--- a/llvm/test/CodeGen/AArch64/lround-conv-fp16-win.ll
+++ b/llvm/test/CodeGen/AArch64/lround-conv-fp16-win.ll
@@ -22,6 +22,8 @@ entry:
 ; CHECK-LABEL: testmhxs:
 ; CHECK:       fcvtas  w8, h0
 ; CHECK-NEXT:  sxtw    x0, w8
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmhxs(half %x) {
 entry:

diff  --git a/llvm/test/CodeGen/AArch64/lround-conv-win.ll b/llvm/test/CodeGen/AArch64/lround-conv-win.ll
index 8bc9213fdced..b815f2a29249 100644
--- a/llvm/test/CodeGen/AArch64/lround-conv-win.ll
+++ b/llvm/test/CodeGen/AArch64/lround-conv-win.ll
@@ -3,6 +3,8 @@
 ; CHECK-LABEL: testmsxs:
 ; CHECK:       fcvtas  w8, s0
 ; CHECK-NEXT:  sxtw    x0, w8
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmsxs(float %x) {
 entry:
@@ -13,6 +15,8 @@ entry:
 
 ; CHECK-LABEL: testmsws:
 ; CHECK:       fcvtas  w0, s0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i32 @testmsws(float %x) {
 entry:
@@ -23,6 +27,8 @@ entry:
 ; CHECK-LABEL: testmsxd:
 ; CHECK:       fcvtas  w8, d0
 ; CHECK-NEXT:  sxtw    x0, w8
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i64 @testmsxd(double %x) {
 entry:
@@ -33,6 +39,8 @@ entry:
 
 ; CHECK-LABEL: testmswd:
 ; CHECK:       fcvtas  w0, d0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT:  ret
 define i32 @testmswd(double %x) {
 entry:

diff  --git a/llvm/test/CodeGen/AArch64/powi-windows.ll b/llvm/test/CodeGen/AArch64/powi-windows.ll
index 859d772b447a..809563f3e9e0 100644
--- a/llvm/test/CodeGen/AArch64/powi-windows.ll
+++ b/llvm/test/CodeGen/AArch64/powi-windows.ll
@@ -11,6 +11,8 @@ entry:
 
 ; CHECK-LABEL: d:
 ; CHECK: scvtf d1, w0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT: b pow
 
 define float @f(float %f, i32 %i) {
@@ -21,6 +23,8 @@ entry:
 
 ; CHECK-LABEL: f:
 ; CHECK: scvtf s1, w0
+; CHECK-NEXT:  .seh_startepilogue
+; CHECK-NEXT:  .seh_endepilogue
 ; CHECK-NEXT: b powf
 
 define float @g(double %d, i32 %i) {

diff  --git a/llvm/test/CodeGen/AArch64/seh_funclet_x1.ll b/llvm/test/CodeGen/AArch64/seh_funclet_x1.ll
index 8f2a4dade309..1f524716be9a 100644
--- a/llvm/test/CodeGen/AArch64/seh_funclet_x1.ll
+++ b/llvm/test/CodeGen/AArch64/seh_funclet_x1.ll
@@ -6,6 +6,8 @@
 ; CHECK:      ?dtor$3@?0?main at 4HA":
 ; CHECK:      .seh_proc "?dtor$3@?0?main at 4HA"
 ; CHECK:      stp     x29, x30, [sp, #-16]!   ; 16-byte Folded Spill
+; CHECK-NEXT: .seh_save_fplr_x 16
+; CHECK-NEXT: .seh_endprologue
 ; CHECK-NEXT: mov     x29, x1
 
 target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"

diff  --git a/llvm/test/CodeGen/AArch64/win64-jumptable.ll b/llvm/test/CodeGen/AArch64/win64-jumptable.ll
index 5a89c2390818..0c61bcd52366 100644
--- a/llvm/test/CodeGen/AArch64/win64-jumptable.ll
+++ b/llvm/test/CodeGen/AArch64/win64-jumptable.ll
@@ -37,6 +37,7 @@ declare void @g(i32, i32)
 ; CHECK:    f:
 ; CHECK:    .seh_proc f
 ; CHECK:    b g
+; CHECK-NEXT:  .seh_endfunclet
 ; CHECK-NEXT: .p2align  2
 ; CHECK-NEXT: .LJTI0_0:
 ; CHECK:    .word .LBB0_2-.LJTI0_0

diff  --git a/llvm/test/CodeGen/AArch64/win_cst_pool.ll b/llvm/test/CodeGen/AArch64/win_cst_pool.ll
index 5d9eed408d40..6065b5f344ce 100644
--- a/llvm/test/CodeGen/AArch64/win_cst_pool.ll
+++ b/llvm/test/CodeGen/AArch64/win_cst_pool.ll
@@ -12,6 +12,8 @@ define double @double() {
 ; CHECK:      double:
 ; CHECK:               adrp    x8, __real at 2000000000800001
 ; CHECK-NEXT:          ldr     d0, [x8, __real at 2000000000800001]
+; CHECK-NEXT:          .seh_startepilogue
+; CHECK-NEXT:          .seh_endepilogue
 ; CHECK-NEXT:          ret
 
 ; MINGW:              .section        .rdata,"dr"
@@ -21,4 +23,6 @@ define double @double() {
 ; MINGW:      double:
 ; MINGW:               adrp    x8, [[LABEL]]
 ; MINGW-NEXT:          ldr     d0, [x8, [[LABEL]]]
+; MINGW-NEXT:          .seh_startepilogue
+; MINGW-NEXT:          .seh_endepilogue
 ; MINGW-NEXT:          ret

diff  --git a/llvm/test/CodeGen/AArch64/windows-extern-weak.ll b/llvm/test/CodeGen/AArch64/windows-extern-weak.ll
index e1e6de496046..3b4f4eeda5ea 100644
--- a/llvm/test/CodeGen/AArch64/windows-extern-weak.ll
+++ b/llvm/test/CodeGen/AArch64/windows-extern-weak.ll
@@ -5,13 +5,18 @@
 define void @func() {
 ; CHECK-LABEL: func:
 ; CHECK:      str x30, [sp, #-16]!
+; CHECK-NEXT: .seh_save_reg_x x30, 16
+; CHECK-NEXT: .seh_endprologue
 ; CHECK-NEXT: adrp x8, .refptr.weakfunc
 ; CHECK-NEXT: ldr x8, [x8, .refptr.weakfunc]
 ; CHECK-NEXT: cbz     x8, .LBB0_2
 ; CHECK-NEXT: ; %bb.1:
 ; CHECK-NEXT: blr     x8
 ; CHECK-NEXT: .LBB0_2:
+; CHECK-NEXT: .seh_startepilogue
 ; CHECK-NEXT: ldr x30, [sp], #16
+; CHECK-NEXT: .seh_save_reg_x x30, 16
+; CHECK-NEXT: .seh_endepilogue
 ; CHECK-NEXT: ret
 
   br i1 icmp ne (void ()* @weakfunc, void ()* null), label %1, label %2

diff  --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-cbz.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-cbz.ll
index cbed64ab99e3..ae11082e88a6 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-cbz.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-cbz.ll
@@ -5,7 +5,10 @@
 ; after the frame setup.)
 
 ; CHECK: stp     x29, x30, [sp, #-32]!
+; CHECK-NEXT: .seh_save_fplr_x 32
 ; CHECK-NEXT: mov     x29, sp
+; CHECK-NEXT: .seh_set_fp
+; CHECK-NEXT: .seh_endprologue
 ; CHECK-NEXT: mov     x1, #-2
 ; CHECK-NEXT: stur    x1, [x29, #16]
 ; CHECK-NEXT: cbz     w0, .LBB0_2

diff  --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
index 9d3af8043d7a..bf1ebaa3d277 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-nobase.ll
@@ -7,12 +7,17 @@
 ; Check that we compute the address relative to fp.
 ; CHECK-LABEL: "?catch$2@?0??a@@YAXXZ at 4HA":
 ; CHECK:             stp     x29, x30, [sp, #-16]!   ; 16-byte Folded Spill
+; CHECK-NEXT:        .seh_save_fplr_x 16
+; CHECK-NEXT:        .seh_endprologue
 ; CHECK-NEXT:        sub     x0, x29, #16            ; =16
 ; CHECK-NEXT:        mov     x1, xzr
 ; CHECK-NEXT:        bl      "?bb@@YAXPEAHH at Z"
 ; CHECK-NEXT:        adrp    x0, .LBB0_1
 ; CHECK-NEXT:        add     x0, x0, .LBB0_1
+; CHECK-NEXT:        .seh_startepilogue
 ; CHECK-NEXT:        ldp     x29, x30, [sp], #16     ; 16-byte Folded Reload
+; CHECK-NEXT:        .seh_save_fplr_x 16
+; CHECK-NEXT:        .seh_endepilogue
 ; CHECK-NEXT:        ret
 
 target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"

diff  --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
index a66ec10748e7..e81932112613 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-realign.ll
@@ -10,16 +10,25 @@
 ; epilogue should be symmetrical.
 ; CHECK-LABEL: "?catch$2@?0??a@@YAXXZ at 4HA":
 ; CHECK:      stp     x29, x30, [sp, #-32]!
+; CHECK-NEXT: .seh_save_fplr_x 32
 ; CHECK-NEXT: str     x28, [sp, #16]
+; CHECK-NEXT: .seh_save_reg x28, 16
 ; CHECK-NEXT: str     x19, [sp, #24]
+; CHECK-NEXT: .seh_save_reg x19, 24
+; CHECK-NEXT: .seh_endprologue
 ; CHECK-NEXT: add     x0, x19, #0
 ; CHECK-NEXT: mov     w1, wzr
 ; CHECK-NEXT: bl      "?bb@@YAXPEAHH at Z"
 ; CHECK-NEXT: adrp    x0, .LBB0_1
 ; CHECK-NEXT: add     x0, x0, .LBB0_1
+; CHECK-NEXT: .seh_startepilogue
 ; CHECK-NEXT: ldr     x19, [sp, #24]
+; CHECK-NEXT: .seh_save_reg x19, 24
 ; CHECK-NEXT: ldr     x28, [sp, #16]
+; CHECK-NEXT: .seh_save_reg x28, 16
 ; CHECK-NEXT: ldp     x29, x30, [sp], #32
+; CHECK-NEXT: .seh_save_fplr_x 32
+; CHECK-NEXT: .seh_endepilogue
 ; CHECK-NEXT: ret
 
 

diff  --git a/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll b/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
index 7ae5ca54ff91..c4974636d879 100644
--- a/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
+++ b/llvm/test/CodeGen/AArch64/wineh-try-catch-vla.ll
@@ -9,12 +9,17 @@
 ; (Funclets aren't allowed to contain dynamic allocas.)
 ; CHECK-LABEL: "?catch$2@?0??a@@YAXXZ at 4HA":
 ; CHECK:      stp     x29, x30, [sp, #-16]!
+; CHECK-NEXT: .seh_save_fplr_x 16
+; CHECK-NEXT: .seh_endprologue
 ; CHECK-NEXT: ldur    x0, [x29, #-8]
 ; CHECK-NEXT: mov     x1, x0
 ; CHECK-NEXT: bl      "?bb@@YAXPEAHH at Z"
 ; CHECK-NEXT: adrp    x0, .LBB0_1
 ; CHECK-NEXT: add     x0, x0, .LBB0_1
+; CHECK-NEXT: .seh_startepilogue
 ; CHECK-NEXT: ldp     x29, x30, [sp], #16
+; CHECK-NEXT: .seh_save_fplr_x 16
+; CHECK-NEXT: .seh_endepilogue
 ; CHECK-NEXT: ret
 
 target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"

diff  --git a/llvm/test/CodeGen/AArch64/wineh1.mir b/llvm/test/CodeGen/AArch64/wineh1.mir
index deaff395ca69..aed1550c54f7 100644
--- a/llvm/test/CodeGen/AArch64/wineh1.mir
+++ b/llvm/test/CodeGen/AArch64/wineh1.mir
@@ -6,6 +6,10 @@
 # documented at:
 # https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
 
+# Also test the generated assembler SEH directives.
+# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog -filetype=asm  \
+# RUN:   | FileCheck %s --check-prefix=ASM
+
 # We expect to see the following in the .xdata section:
 
 # CHECK: 	 ExceptionData {
@@ -47,6 +51,33 @@
 # CHECK-LDSTOPT: name: test
 # CHECK-LDSTOPT: frame-setup STRXui killed $x21, $sp, 6
 # CHECK-LDSTOPT: frame-setup STRXui killed $x22, $sp, 7
+
+# ASM-LABEL: test:
+# ASM: .seh_proc test
+# ASM: .seh_save_regp_x x27, 80
+# ASM: .seh_save_regp x25, 16
+# ASM: .seh_save_regp x23, 32
+# ASM: .seh_save_reg x21, 48
+# ASM: .seh_save_reg x22, 56
+# ASM: .seh_save_regp x19, 64
+# ASM: .seh_endprologue
+
+# ASM: .seh_startepilogue
+# ASM: .seh_save_regp x19, 64
+# ASM: .seh_save_reg x21, 48
+# ASM: .seh_nop
+# ASM: .seh_save_reg x22, 56
+# ASM: .seh_save_regp x23, 32
+# ASM: .seh_save_regp x25, 16
+# ASM: .seh_save_regp_x x27, 80
+# ASM: .seh_endepilogue
+
+# ASM: .seh_endfunclet
+# ASM: .section .xdata,"dr"
+# ASM: .seh_handlerdata
+# ASM: .text
+# ASM: .seh_endproc
+
 ...
 ---
 name:            test

diff  --git a/llvm/test/MC/AArch64/seh.s b/llvm/test/MC/AArch64/seh.s
index 7fa9d5e0e151..633eeb50d8dd 100644
--- a/llvm/test/MC/AArch64/seh.s
+++ b/llvm/test/MC/AArch64/seh.s
@@ -1,9 +1,12 @@
-// This test checks that the SEH directives don't cause the assembler to fail.
-// Checking that llvm-readobj doesn't bail out on the unwind data, but not
-// really checking the contents yet.
+// This test checks that the SEH directives emit the correct unwind data.
 
 // RUN: llvm-mc -triple aarch64-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 aarch64-pc-win32 -filetype=asm %s | llvm-mc -triple aarch64-pc-win32 -filetype=obj - | llvm-readobj -S -r -u - | FileCheck %s
+
 // CHECK:      Sections [
 // CHECK:        Section {
 // CHECK:          Name: .text
@@ -17,7 +20,7 @@
 // CHECK-NEXT:   }
 // CHECK:        Section {
 // CHECK:          Name: .xdata
-// CHECK:          RawDataSize: 20
+// CHECK:          RawDataSize: 48
 // CHECK:          RelocationCount: 1
 // CHECK:          Characteristics [
 // CHECK-NEXT:       ALIGN_4BYTES
@@ -38,7 +41,7 @@
 
 // CHECK-NEXT: Relocations [
 // CHECK-NEXT:   Section (4) .xdata {
-// CHECK-NEXT:     0x8 IMAGE_REL_ARM64_ADDR32NB __C_specific_handler
+// CHECK-NEXT:     0x24 IMAGE_REL_ARM64_ADDR32NB __C_specific_handler
 // CHECK-NEXT:   }
 // CHECK-NEXT:   Section (5) .pdata {
 // CHECK-NEXT:     0x0 IMAGE_REL_ARM64_ADDR32NB func
@@ -51,7 +54,43 @@
 // CHECK-NEXT:     Function: func
 // CHECK-NEXT:     ExceptionRecord: .xdata
 // CHECK-NEXT:     ExceptionData {
-// CHECK-NEXT:       FunctionLength: 8
+// CHECK-NEXT:       FunctionLength: 72
+// CHECK:            Prologue [
+// CHECK-NEXT:         0xe3                ; nop
+// CHECK-NEXT:         0xe202              ; add fp, sp, #16
+// CHECK-NEXT:         0xdd41              ; str d13, [sp, #8]
+// CHECK-NEXT:         0xde83              ; str d12, [sp, #-32]!
+// CHECK-NEXT:         0xd882              ; stp d10, d11, [sp, #16]
+// CHECK-NEXT:         0xda03              ; stp d8, d9, [sp, #-32]!
+// CHECK-NEXT:         0x83                ; stp x29, x30, [sp, #-32]!
+// CHECK-NEXT:         0x46                ; stp x29, x30, [sp, #48]
+// CHECK-NEXT:         0xd141              ; str x24, [sp, #8]
+// CHECK-NEXT:         0xd483              ; str x23, [sp, #-32]!
+// CHECK-NEXT:         0xc882              ; stp x21, x22, [sp, #16]
+// CHECK-NEXT:         0xcc03              ; stp x19, x20, [sp, #-32]!
+// CHECK-NEXT:         0x83                ; stp x29, x30, [sp, #-32]!
+// CHECK-NEXT:         0xe1                ; mov fp, sp
+// CHECK-NEXT:         0x01                ; sub sp, #16
+// CHECK-NEXT:         0xe4                ; end
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       EpilogueScopes [
+// CHECK-NEXT:         EpilogueScope {
+// CHECK-NEXT:           StartOffset: 16
+// CHECK-NEXT:           EpilogueStartIndex: 25
+// CHECK-NEXT:           Opcodes [
+// CHECK-NEXT:             0x01                ; add sp, #16
+// CHECK-NEXT:             0xe4                ; end
+// CHECK-NEXT:           ]
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ]
+// CHECK-NEXT:       ExceptionHandler [
+// CHECK-NEXT:         Routine: __C_specific_handler (0x0)
+// CHECK-NEXT:         Parameter: 0x0
+// CHECK-NEXT:       ]
+// CHECK-NEXT:     }
+// CHECK-NEXT:   }
+// CHECK-NEXT: ]
+
 
     .text
     .globl func
@@ -64,13 +103,44 @@ func:
     sub sp, sp, #24
     .seh_stackalloc 24
     mov x29, sp
+    .seh_set_fp
+    stp x29, x30, [sp, #-32]!
+    .seh_save_fplr_x 32
+    stp x19, x20, [sp, #-32]!
+    .seh_save_regp_x x19, 32
+    stp x21, x22, [sp, #16]
+    .seh_save_regp x21, 16
+    str x23, [sp, #-32]!
+    .seh_save_reg_x x23, 32
+    str x24, [sp, #8]
+    .seh_save_reg x24, 8
+    stp x29, x30, [sp, #48]
+    .seh_save_fplr 48
+    stp x29, x30, [sp, #-32]!
+    .seh_save_fplr_x 32
+    stp d8, d9, [sp, #-32]!
+    .seh_save_fregp_x d8, 32
+    stp d10, d11, [sp, #16]
+    .seh_save_fregp d10, 16
+    str d12, [sp, #-32]!
+    .seh_save_freg_x d12, 32
+    str d13, [sp, #8]
+    .seh_save_freg d13, 8
+    add x29, sp, #16
+    .seh_add_fp 16
+    nop
+    .seh_nop
     .seh_endprologue
+    nop
+    .seh_startepilogue
+    add sp, sp, #24
+    .seh_stackalloc 24
+    .seh_endepilogue
+    ret
     .seh_handler __C_specific_handler, @except
     .seh_handlerdata
     .long 0
     .text
-    add sp, sp, #24
-    ret
     .seh_endproc
 
     // Function with no .seh directives; no pdata/xdata entries are


        


More information about the llvm-commits mailing list