diff --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h --- a/llvm/include/llvm/Support/Win64EH.h +++ b/llvm/include/llvm/Support/Win64EH.h @@ -61,6 +61,19 @@ UOP_TrapFrame, UOP_Context, UOP_ClearUnwoundToCall, + 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 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -377,6 +377,20 @@ case Win64EH::UOP_ClearUnwoundToCall: 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; @@ -543,6 +557,37 @@ b = 0xEC; 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 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -218,6 +218,12 @@ bool parseDirectiveSEHMachineFrame(SMLoc L); bool parseDirectiveSEHContext(SMLoc L); bool parseDirectiveSEHClearUnwoundToCall(SMLoc L); + bool parseDirectiveSEHSaveAnyRegI(SMLoc L, bool Writeback); + bool parseDirectiveSEHSaveAnyRegIP(SMLoc L, bool Writeback); + bool parseDirectiveSEHSaveAnyRegD(SMLoc L, bool Writeback); + bool parseDirectiveSEHSaveAnyRegDP(SMLoc L, bool Writeback); + bool parseDirectiveSEHSaveAnyRegQ(SMLoc L, bool Writeback); + bool parseDirectiveSEHSaveAnyRegQP(SMLoc L, bool Writeback); bool validateInstruction(MCInst &Inst, SMLoc &IDLoc, SmallVectorImpl &Loc); @@ -6099,6 +6105,30 @@ parseDirectiveSEHContext(Loc); else if (IDVal == ".seh_clear_unwound_to_call") parseDirectiveSEHClearUnwoundToCall(Loc); + else if (IDVal ==".seh_save_any_reg_i") + parseDirectiveSEHSaveAnyRegI(Loc, false); + else if (IDVal ==".seh_save_any_reg_ip") + parseDirectiveSEHSaveAnyRegIP(Loc, false); + else if (IDVal ==".seh_save_any_reg_d") + parseDirectiveSEHSaveAnyRegD(Loc, false); + else if (IDVal ==".seh_save_any_reg_dp") + parseDirectiveSEHSaveAnyRegDP(Loc, false); + else if (IDVal ==".seh_save_any_reg_q") + parseDirectiveSEHSaveAnyRegQ(Loc, false); + else if (IDVal ==".seh_save_any_reg_qp") + parseDirectiveSEHSaveAnyRegQP(Loc, false); + else if (IDVal ==".seh_save_any_reg_ix") + parseDirectiveSEHSaveAnyRegI(Loc, true); + else if (IDVal ==".seh_save_any_reg_ipx") + parseDirectiveSEHSaveAnyRegIP(Loc, true); + else if (IDVal ==".seh_save_any_reg_dx") + parseDirectiveSEHSaveAnyRegD(Loc, true); + else if (IDVal ==".seh_save_any_reg_dpx") + parseDirectiveSEHSaveAnyRegDP(Loc, true); + else if (IDVal ==".seh_save_any_reg_qx") + parseDirectiveSEHSaveAnyRegQ(Loc, true); + else if (IDVal ==".seh_save_any_reg_qpx") + parseDirectiveSEHSaveAnyRegQP(Loc, true); else return true; } else @@ -6775,6 +6805,102 @@ return false; } +/// parseDirectiveSEHSaveAnyRegI +/// ::= .seh_save_any_reg_i +/// ::= .seh_save_any_reg_ix +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegI(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X0, AArch64::LR) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegIX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegI(Reg, Offset); + return false; +} + +/// parseDirectiveSEHSaveAnyRegIP +/// ::= .seh_save_any_reg_ip +/// ::= .seh_save_any_reg_ipx +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegIP(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X0, AArch64::FP) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegIPX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegIP(Reg, Offset); + return false; +} + +/// parseDirectiveSEHSaveAnyRegD +/// ::= .seh_save_any_reg_d +/// ::= .seh_save_any_reg_dx +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegD(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D0, AArch64::D31) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegDX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegD(Reg, Offset); + return false; +} + +/// parseDirectiveSEHSaveAnyRegDP +/// ::= .seh_save_any_reg_dp +/// ::= .seh_save_any_reg_dpx +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegDP(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::D0, AArch64::D0, AArch64::D30) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegDPX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegDP(Reg, Offset); + return false; +} + +/// parseDirectiveSEHSaveAnyRegQ +/// ::= .seh_save_any_reg_q +/// ::= .seh_save_any_reg_qx +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegQ(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::Q0, AArch64::Q0, AArch64::Q31) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegQX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegQ(Reg, Offset); + return false; +} + +/// parseDirectiveSEHSaveAnyRegQP +/// ::= .seh_save_any_reg_qp +/// ::= .seh_save_any_reg_qpx +bool AArch64AsmParser::parseDirectiveSEHSaveAnyRegQP(SMLoc L, bool Writeback) { + unsigned Reg; + int64_t Offset; + if (parseRegisterInRange(Reg, AArch64::Q0, AArch64::Q0, AArch64::Q30) || + parseComma() || parseImmExpr(Offset)) + return true; + if (Writeback) + getTargetStreamer().emitARM64WinCFISaveAnyRegQPX(Reg, Offset); + else + getTargetStreamer().emitARM64WinCFISaveAnyRegQP(Reg, Offset); + 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 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -108,6 +108,44 @@ OS << "\t.seh_clear_unwound_to_call\n"; } + void emitARM64WinCFISaveAnyRegI(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_i\tx" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegIP(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_ip\tx" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegD(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_d\td" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegDP(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_dp\td" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegQ(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_q\tq" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegQP(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_qp\tq" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegIX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_ix\tx" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegIPX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_ipx\tx" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegDX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_dx\td" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegDPX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_dpx\td" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegQX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_qx\tq" << Reg << ", " << Offset << "\n"; + } + void emitARM64WinCFISaveAnyRegQPX(unsigned Reg, int Offset) override { + OS << "\t.seh_save_any_reg_qpx\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 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -67,6 +67,18 @@ virtual void emitARM64WinCFIMachineFrame() {} virtual void emitARM64WinCFIContext() {} virtual void emitARM64WinCFIClearUnwoundToCall() {} + 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; @@ -120,6 +132,18 @@ void emitARM64WinCFIMachineFrame() override; void emitARM64WinCFIContext() override; void emitARM64WinCFIClearUnwoundToCall() 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 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp @@ -221,6 +221,67 @@ emitARM64WinUnwindCode(Win64EH::UOP_ClearUnwoundToCall, -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 --- 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: 52 +// 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: 0x28 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: 100 +// CHECK-NEXT: FunctionLength: 148 // 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: 0xec ; clear unwound to call // CHECK-NEXT: 0xea ; context // CHECK-NEXT: 0xe9 ; machine frame @@ -80,9 +92,15 @@ // CHECK-NEXT: 0x01 ; sub sp, #16 // CHECK-NEXT: 0xe4 ; end // CHECK-NEXT: ] -// CHECK-NEXT: Epilogue [ -// CHECK-NEXT: 0x01 ; add sp, #16 -// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: EpilogueScopes [ +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 35 +// CHECK-NEXT: EpilogueStartIndex: 67 +// 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) @@ -145,6 +163,30 @@ .seh_context nop .seh_clear_unwound_to_call + nop + .seh_save_any_reg_i x0, 64 + nop + .seh_save_any_reg_ip x1, 64 + nop + .seh_save_any_reg_d d29, 64 + nop + .seh_save_any_reg_dp d4, 64 + nop + .seh_save_any_reg_q q30, 64 + nop + .seh_save_any_reg_qp q3, 64 + nop + .seh_save_any_reg_ix lr, 64 + nop + .seh_save_any_reg_ipx fp, 64 + nop + .seh_save_any_reg_dx d31, 64 + nop + .seh_save_any_reg_dpx d2, 64 + nop + .seh_save_any_reg_qx q29, 64 + nop + .seh_save_any_reg_qpx q9, 64 .seh_endprologue nop .seh_startepilogue