| //===- COFFAsmParser.cpp - COFF Assembly Parser ---------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/MC/MCParser/MCAsmParserExtension.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/MC/MCAsmInfo.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCParser/MCAsmLexer.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCSectionCOFF.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCTargetAsmParser.h" |
| #include "llvm/Support/COFF.h" |
| using namespace llvm; |
| |
| namespace { |
| |
| class COFFAsmParser : public MCAsmParserExtension { |
| template<bool (COFFAsmParser::*Handler)(StringRef, SMLoc)> |
| void AddDirectiveHandler(StringRef Directive) { |
| getParser().AddDirectiveHandler(this, Directive, |
| HandleDirective<COFFAsmParser, Handler>); |
| } |
| |
| bool ParseSectionSwitch(StringRef Section, |
| unsigned Characteristics, |
| SectionKind Kind); |
| |
| virtual void Initialize(MCAsmParser &Parser) { |
| // Call the base implementation. |
| MCAsmParserExtension::Initialize(Parser); |
| |
| AddDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveText>(".text"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveData>(".data"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveBSS>(".bss"); |
| AddDirectiveHandler<&COFFAsmParser::ParseDirectiveDef>(".def"); |
| AddDirectiveHandler<&COFFAsmParser::ParseDirectiveScl>(".scl"); |
| AddDirectiveHandler<&COFFAsmParser::ParseDirectiveType>(".type"); |
| AddDirectiveHandler<&COFFAsmParser::ParseDirectiveEndef>(".endef"); |
| |
| // Win64 EH directives. |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveStartProc>( |
| ".seh_proc"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndProc>( |
| ".seh_endproc"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveStartChained>( |
| ".seh_startchained"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndChained>( |
| ".seh_endchained"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveHandler>( |
| ".seh_handler"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveHandlerData>( |
| ".seh_handlerdata"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectivePushReg>( |
| ".seh_pushreg"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveSetFrame>( |
| ".seh_setframe"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveAllocStack>( |
| ".seh_stackalloc"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveSaveReg>( |
| ".seh_savereg"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveSaveXMM>( |
| ".seh_savexmm"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectivePushFrame>( |
| ".seh_pushframe"); |
| AddDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndProlog>( |
| ".seh_endprologue"); |
| AddDirectiveHandler<&COFFAsmParser::ParseDirectiveSymbolAttribute>(".weak"); |
| } |
| |
| bool ParseSectionDirectiveText(StringRef, SMLoc) { |
| return ParseSectionSwitch(".text", |
| COFF::IMAGE_SCN_CNT_CODE |
| | COFF::IMAGE_SCN_MEM_EXECUTE |
| | COFF::IMAGE_SCN_MEM_READ, |
| SectionKind::getText()); |
| } |
| bool ParseSectionDirectiveData(StringRef, SMLoc) { |
| return ParseSectionSwitch(".data", |
| COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
| | COFF::IMAGE_SCN_MEM_READ |
| | COFF::IMAGE_SCN_MEM_WRITE, |
| SectionKind::getDataRel()); |
| } |
| bool ParseSectionDirectiveBSS(StringRef, SMLoc) { |
| return ParseSectionSwitch(".bss", |
| COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
| | COFF::IMAGE_SCN_MEM_READ |
| | COFF::IMAGE_SCN_MEM_WRITE, |
| SectionKind::getBSS()); |
| } |
| |
| bool ParseDirectiveDef(StringRef, SMLoc); |
| bool ParseDirectiveScl(StringRef, SMLoc); |
| bool ParseDirectiveType(StringRef, SMLoc); |
| bool ParseDirectiveEndef(StringRef, SMLoc); |
| |
| // Win64 EH directives. |
| bool ParseSEHDirectiveStartProc(StringRef, SMLoc); |
| bool ParseSEHDirectiveEndProc(StringRef, SMLoc); |
| bool ParseSEHDirectiveStartChained(StringRef, SMLoc); |
| bool ParseSEHDirectiveEndChained(StringRef, SMLoc); |
| bool ParseSEHDirectiveHandler(StringRef, SMLoc); |
| bool ParseSEHDirectiveHandlerData(StringRef, SMLoc); |
| bool ParseSEHDirectivePushReg(StringRef, SMLoc); |
| bool ParseSEHDirectiveSetFrame(StringRef, SMLoc); |
| bool ParseSEHDirectiveAllocStack(StringRef, SMLoc); |
| bool ParseSEHDirectiveSaveReg(StringRef, SMLoc); |
| bool ParseSEHDirectiveSaveXMM(StringRef, SMLoc); |
| bool ParseSEHDirectivePushFrame(StringRef, SMLoc); |
| bool ParseSEHDirectiveEndProlog(StringRef, SMLoc); |
| |
| bool ParseAtUnwindOrAtExcept(bool &unwind, bool &except); |
| bool ParseSEHRegisterNumber(unsigned &RegNo); |
| bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc); |
| public: |
| COFFAsmParser() {} |
| }; |
| |
| } // end annonomous namespace. |
| |
| /// ParseDirectiveSymbolAttribute |
| /// ::= { ".weak", ... } [ identifier ( , identifier )* ] |
| bool COFFAsmParser::ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) { |
| MCSymbolAttr Attr = StringSwitch<MCSymbolAttr>(Directive) |
| .Case(".weak", MCSA_Weak) |
| .Default(MCSA_Invalid); |
| assert(Attr != MCSA_Invalid && "unexpected symbol attribute directive!"); |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { |
| for (;;) { |
| StringRef Name; |
| |
| if (getParser().ParseIdentifier(Name)) |
| return TokError("expected identifier in directive"); |
| |
| MCSymbol *Sym = getContext().GetOrCreateSymbol(Name); |
| |
| getStreamer().EmitSymbolAttribute(Sym, Attr); |
| |
| if (getLexer().is(AsmToken::EndOfStatement)) |
| break; |
| |
| if (getLexer().isNot(AsmToken::Comma)) |
| return TokError("unexpected token in directive"); |
| Lex(); |
| } |
| } |
| |
| Lex(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSectionSwitch(StringRef Section, |
| unsigned Characteristics, |
| SectionKind Kind) { |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in section switching directive"); |
| Lex(); |
| |
| getStreamer().SwitchSection(getContext().getCOFFSection( |
| Section, Characteristics, Kind)); |
| |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseDirectiveDef(StringRef, SMLoc) { |
| StringRef SymbolName; |
| |
| if (getParser().ParseIdentifier(SymbolName)) |
| return TokError("expected identifier in directive"); |
| |
| MCSymbol *Sym = getContext().GetOrCreateSymbol(SymbolName); |
| |
| getStreamer().BeginCOFFSymbolDef(Sym); |
| |
| Lex(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseDirectiveScl(StringRef, SMLoc) { |
| int64_t SymbolStorageClass; |
| if (getParser().ParseAbsoluteExpression(SymbolStorageClass)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitCOFFSymbolStorageClass(SymbolStorageClass); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseDirectiveType(StringRef, SMLoc) { |
| int64_t Type; |
| if (getParser().ParseAbsoluteExpression(Type)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitCOFFSymbolType(Type); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseDirectiveEndef(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EndCOFFSymbolDef(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveStartProc(StringRef, SMLoc) { |
| StringRef SymbolID; |
| if (getParser().ParseIdentifier(SymbolID)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| MCSymbol *Symbol = getContext().GetOrCreateSymbol(SymbolID); |
| |
| Lex(); |
| getStreamer().EmitWin64EHStartProc(Symbol); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveEndProc(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EmitWin64EHEndProc(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveStartChained(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EmitWin64EHStartChained(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveEndChained(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EmitWin64EHEndChained(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveHandler(StringRef, SMLoc) { |
| StringRef SymbolID; |
| if (getParser().ParseIdentifier(SymbolID)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::Comma)) |
| return TokError("you must specify one or both of @unwind or @except"); |
| Lex(); |
| bool unwind = false, except = false; |
| if (ParseAtUnwindOrAtExcept(unwind, except)) |
| return true; |
| if (getLexer().is(AsmToken::Comma)) { |
| Lex(); |
| if (ParseAtUnwindOrAtExcept(unwind, except)) |
| return true; |
| } |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| MCSymbol *handler = getContext().GetOrCreateSymbol(SymbolID); |
| |
| Lex(); |
| getStreamer().EmitWin64EHHandler(handler, unwind, except); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveHandlerData(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EmitWin64EHHandlerData(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectivePushReg(StringRef, SMLoc L) { |
| unsigned Reg; |
| if (ParseSEHRegisterNumber(Reg)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitWin64EHPushReg(Reg); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveSetFrame(StringRef, SMLoc L) { |
| unsigned Reg; |
| int64_t Off; |
| if (ParseSEHRegisterNumber(Reg)) |
| return true; |
| if (getLexer().isNot(AsmToken::Comma)) |
| return TokError("you must specify a stack pointer offset"); |
| |
| Lex(); |
| SMLoc startLoc = getLexer().getLoc(); |
| if (getParser().ParseAbsoluteExpression(Off)) |
| return true; |
| |
| if (Off & 0x0F) |
| return Error(startLoc, "offset is not a multiple of 16"); |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitWin64EHSetFrame(Reg, Off); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveAllocStack(StringRef, SMLoc) { |
| int64_t Size; |
| SMLoc startLoc = getLexer().getLoc(); |
| if (getParser().ParseAbsoluteExpression(Size)) |
| return true; |
| |
| if (Size & 7) |
| return Error(startLoc, "size is not a multiple of 8"); |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitWin64EHAllocStack(Size); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveSaveReg(StringRef, SMLoc L) { |
| unsigned Reg; |
| int64_t Off; |
| if (ParseSEHRegisterNumber(Reg)) |
| return true; |
| if (getLexer().isNot(AsmToken::Comma)) |
| return TokError("you must specify an offset on the stack"); |
| |
| Lex(); |
| SMLoc startLoc = getLexer().getLoc(); |
| if (getParser().ParseAbsoluteExpression(Off)) |
| return true; |
| |
| if (Off & 7) |
| return Error(startLoc, "size is not a multiple of 8"); |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| // FIXME: Err on %xmm* registers |
| getStreamer().EmitWin64EHSaveReg(Reg, Off); |
| return false; |
| } |
| |
| // FIXME: This method is inherently x86-specific. It should really be in the |
| // x86 backend. |
| bool COFFAsmParser::ParseSEHDirectiveSaveXMM(StringRef, SMLoc L) { |
| unsigned Reg; |
| int64_t Off; |
| if (ParseSEHRegisterNumber(Reg)) |
| return true; |
| if (getLexer().isNot(AsmToken::Comma)) |
| return TokError("you must specify an offset on the stack"); |
| |
| Lex(); |
| SMLoc startLoc = getLexer().getLoc(); |
| if (getParser().ParseAbsoluteExpression(Off)) |
| return true; |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| if (Off & 0x0F) |
| return Error(startLoc, "offset is not a multiple of 16"); |
| |
| Lex(); |
| // FIXME: Err on non-%xmm* registers |
| getStreamer().EmitWin64EHSaveXMM(Reg, Off); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectivePushFrame(StringRef, SMLoc) { |
| bool Code = false; |
| StringRef CodeID; |
| if (getLexer().is(AsmToken::At)) { |
| SMLoc startLoc = getLexer().getLoc(); |
| Lex(); |
| if (!getParser().ParseIdentifier(CodeID)) { |
| if (CodeID != "code") |
| return Error(startLoc, "expected @code"); |
| Code = true; |
| } |
| } |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return TokError("unexpected token in directive"); |
| |
| Lex(); |
| getStreamer().EmitWin64EHPushFrame(Code); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHDirectiveEndProlog(StringRef, SMLoc) { |
| Lex(); |
| getStreamer().EmitWin64EHEndProlog(); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseAtUnwindOrAtExcept(bool &unwind, bool &except) { |
| StringRef identifier; |
| if (getLexer().isNot(AsmToken::At)) |
| return TokError("a handler attribute must begin with '@'"); |
| SMLoc startLoc = getLexer().getLoc(); |
| Lex(); |
| if (getParser().ParseIdentifier(identifier)) |
| return Error(startLoc, "expected @unwind or @except"); |
| if (identifier == "unwind") |
| unwind = true; |
| else if (identifier == "except") |
| except = true; |
| else |
| return Error(startLoc, "expected @unwind or @except"); |
| return false; |
| } |
| |
| bool COFFAsmParser::ParseSEHRegisterNumber(unsigned &RegNo) { |
| SMLoc startLoc = getLexer().getLoc(); |
| if (getLexer().is(AsmToken::Percent)) { |
| const MCRegisterInfo &MRI = getContext().getRegisterInfo(); |
| SMLoc endLoc; |
| unsigned LLVMRegNo; |
| if (getParser().getTargetParser().ParseRegister(LLVMRegNo,startLoc,endLoc)) |
| return true; |
| |
| #if 0 |
| // FIXME: TargetAsmInfo::getCalleeSavedRegs() commits a serious layering |
| // violation so this validation code is disabled. |
| |
| // Check that this is a non-volatile register. |
| const unsigned *NVRegs = TAI.getCalleeSavedRegs(); |
| unsigned i; |
| for (i = 0; NVRegs[i] != 0; ++i) |
| if (NVRegs[i] == LLVMRegNo) |
| break; |
| if (NVRegs[i] == 0) |
| return Error(startLoc, "expected non-volatile register"); |
| #endif |
| |
| int SEHRegNo = MRI.getSEHRegNum(LLVMRegNo); |
| if (SEHRegNo < 0) |
| return Error(startLoc,"register can't be represented in SEH unwind info"); |
| RegNo = SEHRegNo; |
| } |
| else { |
| int64_t n; |
| if (getParser().ParseAbsoluteExpression(n)) |
| return true; |
| if (n > 15) |
| return Error(startLoc, "register number is too high"); |
| RegNo = n; |
| } |
| |
| return false; |
| } |
| |
| namespace llvm { |
| |
| MCAsmParserExtension *createCOFFAsmParser() { |
| return new COFFAsmParser; |
| } |
| |
| } |