From d6481dc88cd133359a7d043bc24ceb7dadfc2466 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Tue, 18 Oct 2022 11:44:01 -0700 Subject: [PATCH] [AArch64][Windows] Add MC support for save_any_reg. 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 | 13 ++++ llvm/lib/MC/MCWin64EH.cpp | 45 +++++++++++ .../Target/AArch64/AsmParser/AArch64AsmParser.cpp | 87 ++++++++++++++++++++++ .../AArch64/MCTargetDesc/AArch64ELFStreamer.cpp | 37 +++++++++ .../AArch64/MCTargetDesc/AArch64TargetStreamer.h | 24 ++++++ .../MCTargetDesc/AArch64WinCOFFStreamer.cpp | 60 +++++++++++++++ llvm/test/MC/AArch64/seh.s | 46 ++++++++++-- 7 files changed, 307 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h index 93401bd..e84fd6d 100644 --- a/llvm/include/llvm/Support/Win64EH.h +++ b/llvm/include/llvm/Support/Win64EH.h @@ -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 diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index 2b152a3..a0ba62a 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -417,6 +417,20 @@ static uint32_t ARM64CountOfUnwindCodes(ArrayRef 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; + } } } diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 6dbc304..9bfd985 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -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 &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, diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp index ec09ca2..e4003a6 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -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); }; diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h index 0a13e85..d6dc4a5 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -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 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); diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp index 6ba230b..4c8c2b4 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp @@ -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 MAB, std::unique_ptr OW, std::unique_ptr Emitter, diff --git a/llvm/test/MC/AArch64/seh.s b/llvm/test/MC/AArch64/seh.s index 81569ba..4faf7da 100644 --- a/llvm/test/MC/AArch64/seh.s +++ b/llvm/test/MC/AArch64/seh.s @@ -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 @@ -54,8 +54,20 @@ // 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 -- 2.7.4