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