| //===- 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; | |
| } | |
| } |