Skip to content

Commit

Permalink
[AArch64] Generate and parse SEH assembly directives
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mstorsjo committed Aug 29, 2020
1 parent 20f7773 commit 5b86d13
Show file tree
Hide file tree
Showing 20 changed files with 519 additions and 9 deletions.
5 changes: 4 additions & 1 deletion llvm/lib/MC/MCAsmStreamer.cpp
Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/MC/MCParser/COFFAsmParser.cpp
Expand Up @@ -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>(
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
292 changes: 292 additions & 0 deletions llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "MCTargetDesc/AArch64AddressingModes.h"
#include "MCTargetDesc/AArch64InstPrinter.h"
#include "MCTargetDesc/AArch64MCExpr.h"
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "MCTargetDesc/AArch64TargetStreamer.h"
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 5b86d13

Please sign in to comment.