[AArch64][Windows] Add MC support for save_any_reg.
authorEli Friedman <efriedma@quicinc.com>
Tue, 18 Oct 2022 18:44:01 +0000 (11:44 -0700)
committerEli Friedman <efriedma@quicinc.com>
Tue, 18 Oct 2022 18:45:27 +0000 (11:45 -0700)
Representing this as 12 separate operations is a bit ugly, but
trying to represent the different modes using a bitfield seemed worse.

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

llvm/include/llvm/Support/Win64EH.h
llvm/lib/MC/MCWin64EH.cpp
llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h
llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp
llvm/test/MC/AArch64/seh.s

index 93401bd..e84fd6d 100644 (file)
@@ -62,6 +62,19 @@ enum UnwindOpcodes {
   UOP_Context,
   UOP_ClearUnwoundToCall,
   UOP_PACSignLR,
+  UOP_SaveAnyRegI,
+  UOP_SaveAnyRegIP,
+  UOP_SaveAnyRegD,
+  UOP_SaveAnyRegDP,
+  UOP_SaveAnyRegQ,
+  UOP_SaveAnyRegQP,
+  UOP_SaveAnyRegIX,
+  UOP_SaveAnyRegIPX,
+  UOP_SaveAnyRegDX,
+  UOP_SaveAnyRegDPX,
+  UOP_SaveAnyRegQX,
+  UOP_SaveAnyRegQPX,
+
   // The following set of unwind opcodes is for ARM.  They are documented at
   // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
 
index 2b152a3..a0ba62a 100644 (file)
@@ -417,6 +417,20 @@ static uint32_t ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
     case Win64EH::UOP_PACSignLR:
       Count += 1;
       break;
+    case Win64EH::UOP_SaveAnyRegI:
+    case Win64EH::UOP_SaveAnyRegIP:
+    case Win64EH::UOP_SaveAnyRegD:
+    case Win64EH::UOP_SaveAnyRegDP:
+    case Win64EH::UOP_SaveAnyRegQ:
+    case Win64EH::UOP_SaveAnyRegQP:
+    case Win64EH::UOP_SaveAnyRegIX:
+    case Win64EH::UOP_SaveAnyRegIPX:
+    case Win64EH::UOP_SaveAnyRegDX:
+    case Win64EH::UOP_SaveAnyRegDPX:
+    case Win64EH::UOP_SaveAnyRegQX:
+    case Win64EH::UOP_SaveAnyRegQPX:
+      Count += 3;
+      break;
     }
   }
   return Count;
@@ -587,6 +601,37 @@ static void ARM64EmitUnwindCode(MCStreamer &streamer,
     b = 0xFC;
     streamer.emitInt8(b);
     break;
+  case Win64EH::UOP_SaveAnyRegI:
+  case Win64EH::UOP_SaveAnyRegIP:
+  case Win64EH::UOP_SaveAnyRegD:
+  case Win64EH::UOP_SaveAnyRegDP:
+  case Win64EH::UOP_SaveAnyRegQ:
+  case Win64EH::UOP_SaveAnyRegQP:
+  case Win64EH::UOP_SaveAnyRegIX:
+  case Win64EH::UOP_SaveAnyRegIPX:
+  case Win64EH::UOP_SaveAnyRegDX:
+  case Win64EH::UOP_SaveAnyRegDPX:
+  case Win64EH::UOP_SaveAnyRegQX:
+  case Win64EH::UOP_SaveAnyRegQPX: {
+    // This assumes the opcodes are listed in the enum in a particular order.
+    int Op = inst.Operation - Win64EH::UOP_SaveAnyRegI;
+    int Writeback = Op / 6;
+    int Paired = Op % 2;
+    int Mode = (Op / 2) % 3;
+    int Offset = inst.Offset >> 3;
+    if (Writeback || Paired || Mode == 2)
+      Offset >>= 1;
+    if (Writeback)
+      --Offset;
+    b = 0xE7;
+    streamer.emitInt8(b);
+    assert(inst.Register < 32);
+    b = inst.Register | (Writeback << 5) | (Paired << 6);
+    streamer.emitInt8(b);
+    b = Offset | (Mode << 6);
+    streamer.emitInt8(b);
+    break;
+  }
   }
 }
 
index 6dbc304..9bfd985 100644 (file)
@@ -219,6 +219,7 @@ private:
   bool parseDirectiveSEHContext(SMLoc L);
   bool parseDirectiveSEHClearUnwoundToCall(SMLoc L);
   bool parseDirectiveSEHPACSignLR(SMLoc L);
+  bool parseDirectiveSEHSaveAnyReg(SMLoc L, bool Paired, bool Writeback);
 
   bool validateInstruction(MCInst &Inst, SMLoc &IDLoc,
                            SmallVectorImpl<SMLoc> &Loc);
@@ -6102,6 +6103,14 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
       parseDirectiveSEHClearUnwoundToCall(Loc);
     else if (IDVal == ".seh_pac_sign_lr")
       parseDirectiveSEHPACSignLR(Loc);
+    else if (IDVal == ".seh_save_any_reg")
+      parseDirectiveSEHSaveAnyReg(Loc, false, false);
+    else if (IDVal == ".seh_save_any_reg_p")
+      parseDirectiveSEHSaveAnyReg(Loc, true, false);
+    else if (IDVal == ".seh_save_any_reg_x")
+      parseDirectiveSEHSaveAnyReg(Loc, false, true);
+    else if (IDVal == ".seh_save_any_reg_px")
+      parseDirectiveSEHSaveAnyReg(Loc, true, true);
     else
       return true;
   } else
@@ -6785,6 +6794,84 @@ bool AArch64AsmParser::parseDirectiveSEHPACSignLR(SMLoc L) {
   return false;
 }
 
+/// parseDirectiveSEHSaveAnyReg
+/// ::= .seh_save_any_reg
+/// ::= .seh_save_any_reg_p
+/// ::= .seh_save_any_reg_x
+/// ::= .seh_save_any_reg_px
+bool AArch64AsmParser::parseDirectiveSEHSaveAnyReg(SMLoc L, bool Paired,
+                                                   bool Writeback) {
+  unsigned Reg;
+  SMLoc Start, End;
+  int64_t Offset;
+  if (check(ParseRegister(Reg, Start, End), getLoc(), "expected register") ||
+      parseComma() || parseImmExpr(Offset))
+    return true;
+
+  if (Reg == AArch64::FP || Reg == AArch64::LR ||
+      (Reg >= AArch64::X0 && Reg <= AArch64::X28)) {
+    if (Offset < 0 || Offset % (Paired || Writeback ? 16 : 8))
+      return Error(L, "invalid save_any_reg offset");
+    unsigned EncodedReg;
+    if (Reg == AArch64::FP)
+      EncodedReg = 29;
+    else if (Reg == AArch64::LR)
+      EncodedReg = 30;
+    else
+      EncodedReg = Reg - AArch64::X0;
+    if (Paired) {
+      if (Reg == AArch64::LR)
+        return Error(Start, "lr cannot be paired with another register");
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegIPX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegIP(EncodedReg, Offset);
+    } else {
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegIX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegI(EncodedReg, Offset);
+    }
+  } else if (Reg >= AArch64::D0 && Reg <= AArch64::D31) {
+    unsigned EncodedReg = Reg - AArch64::D0;
+    if (Offset < 0 || Offset % (Paired || Writeback ? 16 : 8))
+      return Error(L, "invalid save_any_reg offset");
+    if (Paired) {
+      if (Reg == AArch64::D31)
+        return Error(Start, "d31 cannot be paired with another register");
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegDPX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegDP(EncodedReg, Offset);
+    } else {
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegDX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegD(EncodedReg, Offset);
+    }
+  } else if (Reg >= AArch64::Q0 && Reg <= AArch64::Q31) {
+    unsigned EncodedReg = Reg - AArch64::Q0;
+    if (Offset < 0 || Offset % 16)
+      return Error(L, "invalid save_any_reg offset");
+    if (Paired) {
+      if (Reg == AArch64::Q31)
+        return Error(Start, "q31 cannot be paired with another register");
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegQPX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegQP(EncodedReg, Offset);
+    } else {
+      if (Writeback)
+        getTargetStreamer().emitARM64WinCFISaveAnyRegQX(EncodedReg, Offset);
+      else
+        getTargetStreamer().emitARM64WinCFISaveAnyRegQ(EncodedReg, Offset);
+    }
+  } else {
+    return Error(Start, "save_any_reg register must be x, q or d register");
+  }
+  return false;
+}
+
 bool
 AArch64AsmParser::classifySymbolRef(const MCExpr *Expr,
                                     AArch64MCExpr::VariantKind &ELFRefKind,
index ec09ca2..e4003a6 100644 (file)
@@ -111,6 +111,43 @@ class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
     OS << "\t.seh_pac_sign_lr\n";
   }
 
+  void emitARM64WinCFISaveAnyRegI(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg\tx" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegIP(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_p\tx" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegD(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg\td" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegDP(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_p\td" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegQ(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg\tq" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegQP(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_p\tq" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegIX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_x\tx" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegIPX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_px\tx" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegDX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_x\td" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegDPX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_px\td" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegQX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_x\tq" << Reg << ", " << Offset << "\n";
+  }
+  void emitARM64WinCFISaveAnyRegQPX(unsigned Reg, int Offset) override {
+    OS << "\t.seh_save_any_reg_px\tq" << Reg << ", " << Offset << "\n";
+  }
+
 public:
   AArch64TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
 };
index 0a13e85..d6dc4a5 100644 (file)
@@ -68,6 +68,18 @@ public:
   virtual void emitARM64WinCFIContext() {}
   virtual void emitARM64WinCFIClearUnwoundToCall() {}
   virtual void emitARM64WinCFIPACSignLR() {}
+  virtual void emitARM64WinCFISaveAnyRegI(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegIP(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegD(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegDP(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegQ(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegQP(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegIX(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegIPX(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegDX(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegDPX(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegQX(unsigned Reg, int Offset) {}
+  virtual void emitARM64WinCFISaveAnyRegQPX(unsigned Reg, int Offset) {}
 
 private:
   std::unique_ptr<AssemblerConstantPools> ConstantPools;
@@ -122,6 +134,18 @@ public:
   void emitARM64WinCFIContext() override;
   void emitARM64WinCFIClearUnwoundToCall() override;
   void emitARM64WinCFIPACSignLR() override;
+  void emitARM64WinCFISaveAnyRegI(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegIP(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegD(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegDP(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegQ(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegQP(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegIX(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegIPX(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegDX(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegDPX(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegQX(unsigned Reg, int Offset) override;
+  void emitARM64WinCFISaveAnyRegQPX(unsigned Reg, int Offset) override;
 
 private:
   void emitARM64WinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
index 6ba230b..4c8c2b4 100644 (file)
@@ -227,6 +227,66 @@ void AArch64TargetWinCOFFStreamer::emitARM64WinCFIPACSignLR() {
   emitARM64WinUnwindCode(Win64EH::UOP_PACSignLR, -1, 0);
 }
 
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegI(unsigned Reg,
+                                                              int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegI, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIP(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIP, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegD(unsigned Reg,
+                                                              int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegD, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDP(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDP, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQ(unsigned Reg,
+                                                              int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQ, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQP(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQP, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIX(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIX, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegIPX(unsigned Reg,
+                                                                int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegIPX, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDX(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDX, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegDPX(unsigned Reg,
+                                                                int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegDPX, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQX(unsigned Reg,
+                                                               int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQX, Reg, Offset);
+}
+
+void AArch64TargetWinCOFFStreamer::emitARM64WinCFISaveAnyRegQPX(unsigned Reg,
+                                                                int Offset) {
+  emitARM64WinUnwindCode(Win64EH::UOP_SaveAnyRegQPX, Reg, Offset);
+}
+
 MCWinCOFFStreamer *llvm::createAArch64WinCOFFStreamer(
     MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
     std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter,
index 81569ba..4faf7da 100644 (file)
@@ -20,7 +20,7 @@
 // CHECK-NEXT:   }
 // CHECK:        Section {
 // CHECK:          Name: .xdata
-// CHECK:          RawDataSize: 56
+// CHECK:          RawDataSize: 92
 // CHECK:          RelocationCount: 1
 // CHECK:          Characteristics [
 // CHECK-NEXT:       ALIGN_4BYTES
@@ -41,7 +41,7 @@
 
 // CHECK-NEXT: Relocations [
 // CHECK-NEXT:   Section (4) .xdata {
-// CHECK-NEXT:     0x2C IMAGE_REL_ARM64_ADDR32NB __C_specific_handler
+// CHECK-NEXT:     0x50 IMAGE_REL_ARM64_ADDR32NB __C_specific_handler
 // CHECK-NEXT:   }
 // CHECK-NEXT:   Section (5) .pdata {
 // CHECK-NEXT:     0x0 IMAGE_REL_ARM64_ADDR32NB .text
 // CHECK-NEXT:     Function: func
 // CHECK-NEXT:     ExceptionRecord: .xdata
 // CHECK-NEXT:     ExceptionData {
-// CHECK-NEXT:       FunctionLength: 104
+// CHECK-NEXT:       FunctionLength: 152
 // CHECK:            Prologue [
+// CHECK-NEXT:         0xe76983            ; stp q9, q10, [sp, #-64]!
+// CHECK-NEXT:         0xe73d83            ; str q29, [sp, #-64]!
+// CHECK-NEXT:         0xe76243            ; stp d2, d3, [sp, #-64]!
+// CHECK-NEXT:         0xe73f43            ; str d31, [sp, #-64]!
+// CHECK-NEXT:         0xe77d03            ; stp x29, x30, [sp, #-64]!
+// CHECK-NEXT:         0xe73e03            ; str x30, [sp, #-64]!
+// CHECK-NEXT:         0xe74384            ; stp q3, q4, [sp, #64]
+// CHECK-NEXT:         0xe71e84            ; str q30, [sp, #64]
+// CHECK-NEXT:         0xe74444            ; stp d4, d5, [sp, #64]
+// CHECK-NEXT:         0xe71d48            ; str d29, [sp, #64]
+// CHECK-NEXT:         0xe74104            ; stp x1, x2, [sp, #64]
+// CHECK-NEXT:         0xe70008            ; str x0, [sp, #64]
 // CHECK-NEXT:         0xfc                ; pacibsp
 // CHECK-NEXT:         0xec                ; clear unwound to call
 // CHECK-NEXT:         0xea                ; context
@@ -83,8 +95,8 @@
 // CHECK-NEXT:       ]
 // CHECK-NEXT:       EpilogueScopes [
 // CHECK-NEXT:         EpilogueScope {
-// CHECK-NEXT:           StartOffset: 24
-// CHECK-NEXT:           EpilogueStartIndex: 32
+// CHECK-NEXT:           StartOffset: 36
+// CHECK-NEXT:           EpilogueStartIndex: 68
 // CHECK-NEXT:           Opcodes [
 // CHECK-NEXT:             0x01                ; add sp, #16
 // CHECK-NEXT:             0xe4                ; end
@@ -154,6 +166,30 @@ func:
     .seh_clear_unwound_to_call
     pacibsp
     .seh_pac_sign_lr
+    nop
+    .seh_save_any_reg x0, 64
+    nop
+    .seh_save_any_reg_p x1, 64
+    nop
+    .seh_save_any_reg d29, 64
+    nop
+    .seh_save_any_reg_p d4, 64
+    nop
+    .seh_save_any_reg q30, 64
+    nop
+    .seh_save_any_reg_p q3, 64
+    nop
+    .seh_save_any_reg_x lr, 64
+    nop
+    .seh_save_any_reg_px fp, 64
+    nop
+    .seh_save_any_reg_x d31, 64
+    nop
+    .seh_save_any_reg_px d2, 64
+    nop
+    .seh_save_any_reg_x q29, 64
+    nop
+    .seh_save_any_reg_px q9, 64
     .seh_endprologue
     nop
     .seh_startepilogue