Skip to content

Commit

Permalink
[AArch64] Add asm directives for the remaining SEH unwind codes
Browse files Browse the repository at this point in the history
Add support in llvm-readobj for displaying them and support in the
asm parsser, AArch64TargetStreamer and MCWin64EH for emitting them.

The directives for the remaining basic opcodes have names that
match the opcode in the documentation.

The directives for custom stack cases, that are named
MSFT_OP_TRAP_FRAME, MSFT_OP_MACHINE_FRAME, MSFT_OP_CONTEXT
and MSFT_OP_CLEAR_UNWOUND_TO_CALL, are given matching assembler
directive names that fit into the rest of the opcode naming;
.seh_trap_frame, .seh_context, .seh_clear_unwound_to_call

The opcode MSFT_OP_MACHINE_FRAME is mapped to the existing
opecode enum UOP_PushMachFrame that is used on x86_64, and also
uses the corresponding existing x86_64 directive name
.seh_pushframe.

Differential Revision: https://reviews.llvm.org/D86889
  • Loading branch information
mstorsjo committed Sep 3, 2020
1 parent e123959 commit f5e2ea9
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 6 deletions.
8 changes: 7 additions & 1 deletion llvm/include/llvm/Support/Win64EH.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,26 @@ enum UnwindOpcodes {
// The following set of unwind opcodes is for ARM64. They are documented at
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
UOP_AllocMedium,
UOP_SaveR19R20X,
UOP_SaveFPLRX,
UOP_SaveFPLR,
UOP_SaveReg,
UOP_SaveRegX,
UOP_SaveRegP,
UOP_SaveRegPX,
UOP_SaveLRPair,
UOP_SaveFReg,
UOP_SaveFRegX,
UOP_SaveFRegP,
UOP_SaveFRegPX,
UOP_SetFP,
UOP_AddFP,
UOP_Nop,
UOP_End
UOP_End,
UOP_SaveNext,
UOP_TrapFrame,
UOP_Context,
UOP_ClearUnwoundToCall
};

/// UnwindCode - This union describes a single operation in a function prolog,
Expand Down
56 changes: 56 additions & 0 deletions llvm/lib/MC/MCWin64EH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ ARM64CountOfUnwindCodes(const std::vector<WinEH::Instruction> &Insns) {
case Win64EH::UOP_AllocLarge:
Count += 4;
break;
case Win64EH::UOP_SaveR19R20X:
Count += 1;
break;
case Win64EH::UOP_SaveFPLRX:
Count += 1;
break;
Expand All @@ -298,6 +301,9 @@ ARM64CountOfUnwindCodes(const std::vector<WinEH::Instruction> &Insns) {
case Win64EH::UOP_SaveRegX:
Count += 2;
break;
case Win64EH::UOP_SaveLRPair:
Count += 2;
break;
case Win64EH::UOP_SaveFReg:
Count += 2;
break;
Expand All @@ -322,6 +328,21 @@ ARM64CountOfUnwindCodes(const std::vector<WinEH::Instruction> &Insns) {
case Win64EH::UOP_End:
Count += 1;
break;
case Win64EH::UOP_SaveNext:
Count += 1;
break;
case Win64EH::UOP_TrapFrame:
Count += 1;
break;
case Win64EH::UOP_PushMachFrame:
Count += 1;
break;
case Win64EH::UOP_Context:
Count += 1;
break;
case Win64EH::UOP_ClearUnwoundToCall:
Count += 1;
break;
}
}
return Count;
Expand Down Expand Up @@ -375,6 +396,11 @@ static void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
b = 0xE3;
streamer.emitInt8(b);
break;
case Win64EH::UOP_SaveR19R20X:
b = 0x20;
b |= (inst.Offset >> 3) & 0x1F;
streamer.emitInt8(b);
break;
case Win64EH::UOP_SaveFPLRX:
b = 0x80;
b |= ((inst.Offset - 1) >> 3) & 0x3F;
Expand Down Expand Up @@ -417,6 +443,16 @@ static void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1);
streamer.emitInt8(b);
break;
case Win64EH::UOP_SaveLRPair:
assert(inst.Register >= 19 && "Saved reg must be >= 19");
reg = inst.Register - 19;
assert((reg % 2) == 0 && "Saved reg must be 19+2*X");
reg /= 2;
b = 0xD6 | ((reg & 0x7) >> 2);
streamer.emitInt8(b);
b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
streamer.emitInt8(b);
break;
case Win64EH::UOP_SaveFReg:
assert(inst.Register >= 8 && "Saved dreg must be >= 8");
reg = inst.Register - 8;
Expand Down Expand Up @@ -453,6 +489,26 @@ static void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
b = 0xE4;
streamer.emitInt8(b);
break;
case Win64EH::UOP_SaveNext:
b = 0xE6;
streamer.emitInt8(b);
break;
case Win64EH::UOP_TrapFrame:
b = 0xE8;
streamer.emitInt8(b);
break;
case Win64EH::UOP_PushMachFrame:
b = 0xE9;
streamer.emitInt8(b);
break;
case Win64EH::UOP_Context:
b = 0xEA;
streamer.emitInt8(b);
break;
case Win64EH::UOP_ClearUnwoundToCall:
b = 0xEC;
streamer.emitInt8(b);
break;
}
}

Expand Down
82 changes: 82 additions & 0 deletions llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,21 +186,28 @@ class AArch64AsmParser : public MCTargetAsmParser {

bool parseDirectiveSEHAllocStack(SMLoc L);
bool parseDirectiveSEHPrologEnd(SMLoc L);
bool parseDirectiveSEHSaveR19R20X(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 parseDirectiveSEHSaveLRPair(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 parseDirectiveSEHSaveNext(SMLoc L);
bool parseDirectiveSEHEpilogStart(SMLoc L);
bool parseDirectiveSEHEpilogEnd(SMLoc L);
bool parseDirectiveSEHTrapFrame(SMLoc L);
bool parseDirectiveSEHMachineFrame(SMLoc L);
bool parseDirectiveSEHContext(SMLoc L);
bool parseDirectiveSEHClearUnwoundToCall(SMLoc L);

bool validateInstruction(MCInst &Inst, SMLoc &IDLoc,
SmallVectorImpl<SMLoc> &Loc);
Expand Down Expand Up @@ -5174,6 +5181,8 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
parseDirectiveSEHAllocStack(Loc);
else if (IDVal == ".seh_endprologue")
parseDirectiveSEHPrologEnd(Loc);
else if (IDVal == ".seh_save_r19r20_x")
parseDirectiveSEHSaveR19R20X(Loc);
else if (IDVal == ".seh_save_fplr")
parseDirectiveSEHSaveFPLR(Loc);
else if (IDVal == ".seh_save_fplr_x")
Expand All @@ -5186,6 +5195,8 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
parseDirectiveSEHSaveRegP(Loc);
else if (IDVal == ".seh_save_regp_x")
parseDirectiveSEHSaveRegPX(Loc);
else if (IDVal == ".seh_save_lrpair")
parseDirectiveSEHSaveLRPair(Loc);
else if (IDVal == ".seh_save_freg")
parseDirectiveSEHSaveFReg(Loc);
else if (IDVal == ".seh_save_freg_x")
Expand All @@ -5200,10 +5211,20 @@ bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) {
parseDirectiveSEHAddFP(Loc);
else if (IDVal == ".seh_nop")
parseDirectiveSEHNop(Loc);
else if (IDVal == ".seh_save_next")
parseDirectiveSEHSaveNext(Loc);
else if (IDVal == ".seh_startepilogue")
parseDirectiveSEHEpilogStart(Loc);
else if (IDVal == ".seh_endepilogue")
parseDirectiveSEHEpilogEnd(Loc);
else if (IDVal == ".seh_trap_frame")
parseDirectiveSEHTrapFrame(Loc);
else if (IDVal == ".seh_pushframe")
parseDirectiveSEHMachineFrame(Loc);
else if (IDVal == ".seh_context")
parseDirectiveSEHContext(Loc);
else if (IDVal == ".seh_clear_unwound_to_call")
parseDirectiveSEHClearUnwoundToCall(Loc);
else
return true;
} else
Expand Down Expand Up @@ -5645,6 +5666,16 @@ bool AArch64AsmParser::parseDirectiveSEHPrologEnd(SMLoc L) {
return false;
}

/// parseDirectiveSEHSaveR19R20X
/// ::= .seh_save_r19r20_x
bool AArch64AsmParser::parseDirectiveSEHSaveR19R20X(SMLoc L) {
int64_t Offset;
if (parseImmExpr(Offset))
return true;
getTargetStreamer().EmitARM64WinCFISaveR19R20X(Offset);
return false;
}

/// parseDirectiveSEHSaveFPLR
/// ::= .seh_save_fplr
bool AArch64AsmParser::parseDirectiveSEHSaveFPLR(SMLoc L) {
Expand Down Expand Up @@ -5713,6 +5744,22 @@ bool AArch64AsmParser::parseDirectiveSEHSaveRegPX(SMLoc L) {
return false;
}

/// parseDirectiveSEHSaveLRPair
/// ::= .seh_save_lrpair
bool AArch64AsmParser::parseDirectiveSEHSaveLRPair(SMLoc L) {
unsigned Reg;
int64_t Offset;
L = getLoc();
if (parseRegisterInRange(Reg, AArch64::X0, AArch64::X19, AArch64::LR) ||
parseComma() || parseImmExpr(Offset))
return true;
if (check(((Reg - 19) % 2 != 0), L,
"expected register with even offset from x19"))
return true;
getTargetStreamer().EmitARM64WinCFISaveLRPair(Reg, Offset);
return false;
}

/// parseDirectiveSEHSaveFReg
/// ::= .seh_save_freg
bool AArch64AsmParser::parseDirectiveSEHSaveFReg(SMLoc L) {
Expand Down Expand Up @@ -5785,6 +5832,13 @@ bool AArch64AsmParser::parseDirectiveSEHNop(SMLoc L) {
return false;
}

/// parseDirectiveSEHSaveNext
/// ::= .seh_save_next
bool AArch64AsmParser::parseDirectiveSEHSaveNext(SMLoc L) {
getTargetStreamer().EmitARM64WinCFISaveNext();
return false;
}

/// parseDirectiveSEHEpilogStart
/// ::= .seh_startepilogue
bool AArch64AsmParser::parseDirectiveSEHEpilogStart(SMLoc L) {
Expand All @@ -5799,6 +5853,34 @@ bool AArch64AsmParser::parseDirectiveSEHEpilogEnd(SMLoc L) {
return false;
}

/// parseDirectiveSEHTrapFrame
/// ::= .seh_trap_frame
bool AArch64AsmParser::parseDirectiveSEHTrapFrame(SMLoc L) {
getTargetStreamer().EmitARM64WinCFITrapFrame();
return false;
}

/// parseDirectiveSEHMachineFrame
/// ::= .seh_pushframe
bool AArch64AsmParser::parseDirectiveSEHMachineFrame(SMLoc L) {
getTargetStreamer().EmitARM64WinCFIMachineFrame();
return false;
}

/// parseDirectiveSEHContext
/// ::= .seh_context
bool AArch64AsmParser::parseDirectiveSEHContext(SMLoc L) {
getTargetStreamer().EmitARM64WinCFIContext();
return false;
}

/// parseDirectiveSEHClearUnwoundToCall
/// ::= .seh_clear_unwound_to_call
bool AArch64AsmParser::parseDirectiveSEHClearUnwoundToCall(SMLoc L) {
getTargetStreamer().EmitARM64WinCFIClearUnwoundToCall();
return false;
}

bool
AArch64AsmParser::classifySymbolRef(const MCExpr *Expr,
AArch64MCExpr::VariantKind &ELFRefKind,
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
void EmitARM64WinCFIAllocStack(unsigned Size) override {
OS << "\t.seh_stackalloc " << Size << "\n";
}
void EmitARM64WinCFISaveR19R20X(int Offset) override {
OS << "\t.seh_save_r19r20_x " << Offset << "\n";
}
void EmitARM64WinCFISaveFPLR(int Offset) override {
OS << "\t.seh_save_fplr " << Offset << "\n";
}
Expand All @@ -68,6 +71,9 @@ class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) override {
OS << "\t.seh_save_regp_x x" << Reg << ", " << Offset << "\n";
}
void EmitARM64WinCFISaveLRPair(unsigned Reg, int Offset) override {
OS << "\t.seh_save_lrpair x" << Reg << ", " << Offset << "\n";
}
void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) override {
OS << "\t.seh_save_freg d" << Reg << ", " << Offset << "\n";
}
Expand All @@ -85,9 +91,16 @@ class AArch64TargetAsmStreamer : public AArch64TargetStreamer {
OS << "\t.seh_add_fp " << Size << "\n";
}
void EmitARM64WinCFINop() override { OS << "\t.seh_nop\n"; }
void EmitARM64WinCFISaveNext() override { OS << "\t.seh_save_next\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"; }
void EmitARM64WinCFITrapFrame() override { OS << "\t.seh_trap_frame\n"; }
void EmitARM64WinCFIMachineFrame() override { OS << "\t.seh_pushframe\n"; }
void EmitARM64WinCFIContext() override { OS << "\t.seh_context\n"; }
void EmitARM64WinCFIClearUnwoundToCall() override {
OS << "\t.seh_clear_unwound_to_call\n";
}

public:
AArch64TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,29 @@ class AArch64TargetStreamer : public MCTargetStreamer {
virtual void emitInst(uint32_t Inst);

virtual void EmitARM64WinCFIAllocStack(unsigned Size) {}
virtual void EmitARM64WinCFISaveR19R20X(int Offset) {}
virtual void EmitARM64WinCFISaveFPLR(int Offset) {}
virtual void EmitARM64WinCFISaveFPLRX(int Offset) {}
virtual void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveLRPair(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) {}
virtual void EmitARM64WinCFISetFP() {}
virtual void EmitARM64WinCFIAddFP(unsigned Size) {}
virtual void EmitARM64WinCFINop() {}
virtual void EmitARM64WinCFISaveNext() {}
virtual void EmitARM64WinCFIPrologEnd() {}
virtual void EmitARM64WinCFIEpilogStart() {}
virtual void EmitARM64WinCFIEpilogEnd() {}
virtual void EmitARM64WinCFITrapFrame() {}
virtual void EmitARM64WinCFIMachineFrame() {}
virtual void EmitARM64WinCFIContext() {}
virtual void EmitARM64WinCFIClearUnwoundToCall() {}

private:
std::unique_ptr<AssemblerConstantPools> ConstantPools;
Expand Down Expand Up @@ -82,22 +89,30 @@ class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer {
// The unwind codes on ARM64 Windows are documented at
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
void EmitARM64WinCFIAllocStack(unsigned Size) override;
void EmitARM64WinCFISaveR19R20X(int Offset) override;
void EmitARM64WinCFISaveFPLR(int Offset) override;
void EmitARM64WinCFISaveFPLRX(int Offset) override;
void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveLRPair(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) override;
void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) override;
void EmitARM64WinCFISetFP() override;
void EmitARM64WinCFIAddFP(unsigned Size) override;
void EmitARM64WinCFINop() override;
void EmitARM64WinCFISaveNext() override;
void EmitARM64WinCFIPrologEnd() override;
void EmitARM64WinCFIEpilogStart() override;
void EmitARM64WinCFIEpilogEnd() override;
void EmitARM64WinCFITrapFrame() override;
void EmitARM64WinCFIMachineFrame() override;
void EmitARM64WinCFIContext() override;
void EmitARM64WinCFIClearUnwoundToCall() override;

private:
void EmitARM64WinUnwindCode(unsigned UnwindCode, int Reg, int Offset);
};
Expand Down
Loading

0 comments on commit f5e2ea9

Please sign in to comment.