blob: 51bb1fe92b73d24f4fa6151e67fb19dc950c2446 [file] [log] [blame]
//===- COFFAsmParser.cpp - COFF Assembly Parser ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDirectives.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSectionCOFF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/SectionKind.h"
#include "llvm/Support/SMLoc.h"
#include <cassert>
#include <cstdint>
#include <limits>
#include <utility>
using namespace llvm;
namespace {
class COFFAsmParser : public MCAsmParserExtension {
template<bool (COFFAsmParser::*HandlerMethod)(StringRef, SMLoc)>
void addDirectiveHandler(StringRef Directive) {
MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair(
this, HandleDirective<COFFAsmParser, HandlerMethod>);
getParser().addDirectiveHandler(Directive, Handler);
}
bool ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind);
bool ParseSectionSwitch(StringRef Section, unsigned Characteristics,
SectionKind Kind, StringRef COMDATSymName,
COFF::COMDATType Type);
bool ParseSectionName(StringRef &SectionName);
bool ParseSectionFlags(StringRef SectionName, StringRef FlagsString,
unsigned *Flags);
void Initialize(MCAsmParser &Parser) override {
// Call the base implementation.
MCAsmParserExtension::Initialize(Parser);
addDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveText>(".text");
addDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveData>(".data");
addDirectiveHandler<&COFFAsmParser::ParseSectionDirectiveBSS>(".bss");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSection>(".section");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveDef>(".def");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveScl>(".scl");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveType>(".type");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveEndef>(".endef");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSecRel32>(".secrel32");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSymIdx>(".symidx");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSafeSEH>(".safeseh");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSecIdx>(".secidx");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveLinkOnce>(".linkonce");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveRVA>(".rva");
addDirectiveHandler<&COFFAsmParser::ParseDirectiveSymbolAttribute>(".weak");
// 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::ParseSEHDirectiveAllocStack>(
".seh_stackalloc");
addDirectiveHandler<&COFFAsmParser::ParseSEHDirectiveEndProlog>(
".seh_endprologue");
}
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::getData());
}
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 ParseDirectiveSection(StringRef, SMLoc);
bool ParseDirectiveDef(StringRef, SMLoc);
bool ParseDirectiveScl(StringRef, SMLoc);
bool ParseDirectiveType(StringRef, SMLoc);
bool ParseDirectiveEndef(StringRef, SMLoc);
bool ParseDirectiveSecRel32(StringRef, SMLoc);
bool ParseDirectiveSecIdx(StringRef, SMLoc);
bool ParseDirectiveSafeSEH(StringRef, SMLoc);
bool ParseDirectiveSymIdx(StringRef, SMLoc);
bool parseCOMDATType(COFF::COMDATType &Type);
bool ParseDirectiveLinkOnce(StringRef, SMLoc);
bool ParseDirectiveRVA(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 ParseSEHDirectiveAllocStack(StringRef, SMLoc);
bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
bool ParseAtUnwindOrAtExcept(bool &unwind, bool &except);
bool ParseSEHRegisterNumber(unsigned &RegNo);
bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc);
public:
COFFAsmParser() = default;
};
} // end anonymous namespace.
static SectionKind computeSectionKind(unsigned Flags) {
if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE)
return SectionKind::getText();
if (Flags & COFF::IMAGE_SCN_MEM_READ &&
(Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0)
return SectionKind::getReadOnly();
return SectionKind::getData();
}
bool COFFAsmParser::ParseSectionFlags(StringRef SectionName,
StringRef FlagsString, unsigned *Flags) {
enum {
None = 0,
Alloc = 1 << 0,
Code = 1 << 1,
Load = 1 << 2,
InitData = 1 << 3,
Shared = 1 << 4,
NoLoad = 1 << 5,
NoRead = 1 << 6,
NoWrite = 1 << 7,
Discardable = 1 << 8,
};
bool ReadOnlyRemoved = false;
unsigned SecFlags = None;
for (char FlagChar : FlagsString) {
switch (FlagChar) {
case 'a':
// Ignored.
break;
case 'b': // bss section
SecFlags |= Alloc;
if (SecFlags & InitData)
return TokError("conflicting section flags 'b' and 'd'.");
SecFlags &= ~Load;
break;
case 'd': // data section
SecFlags |= InitData;
if (SecFlags & Alloc)
return TokError("conflicting section flags 'b' and 'd'.");
SecFlags &= ~NoWrite;
if ((SecFlags & NoLoad) == 0)
SecFlags |= Load;
break;
case 'n': // section is not loaded
SecFlags |= NoLoad;
SecFlags &= ~Load;
break;
case 'D': // discardable
SecFlags |= Discardable;
break;
case 'r': // read-only
ReadOnlyRemoved = false;
SecFlags |= NoWrite;
if ((SecFlags & Code) == 0)
SecFlags |= InitData;
if ((SecFlags & NoLoad) == 0)
SecFlags |= Load;
break;
case 's': // shared section
SecFlags |= Shared | InitData;
SecFlags &= ~NoWrite;
if ((SecFlags & NoLoad) == 0)
SecFlags |= Load;
break;
case 'w': // writable
SecFlags &= ~NoWrite;
ReadOnlyRemoved = true;
break;
case 'x': // executable section
SecFlags |= Code;
if ((SecFlags & NoLoad) == 0)
SecFlags |= Load;
if (!ReadOnlyRemoved)
SecFlags |= NoWrite;
break;
case 'y': // not readable
SecFlags |= NoRead | NoWrite;
break;
default:
return TokError("unknown flag");
}
}
*Flags = 0;
if (SecFlags == None)
SecFlags = InitData;
if (SecFlags & Code)
*Flags |= COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE;
if (SecFlags & InitData)
*Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
if ((SecFlags & Alloc) && (SecFlags & Load) == 0)
*Flags |= COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA;
if (SecFlags & NoLoad)
*Flags |= COFF::IMAGE_SCN_LNK_REMOVE;
if ((SecFlags & Discardable) ||
MCSectionCOFF::isImplicitlyDiscardable(SectionName))
*Flags |= COFF::IMAGE_SCN_MEM_DISCARDABLE;
if ((SecFlags & NoRead) == 0)
*Flags |= COFF::IMAGE_SCN_MEM_READ;
if ((SecFlags & NoWrite) == 0)
*Flags |= COFF::IMAGE_SCN_MEM_WRITE;
if (SecFlags & Shared)
*Flags |= COFF::IMAGE_SCN_MEM_SHARED;
return false;
}
/// 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)) {
while (true) {
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) {
return ParseSectionSwitch(Section, Characteristics, Kind, "", (COFF::COMDATType)0);
}
bool COFFAsmParser::ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind,
StringRef COMDATSymName,
COFF::COMDATType Type) {
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in section switching directive");
Lex();
getStreamer().SwitchSection(getContext().getCOFFSection(
Section, Characteristics, Kind, COMDATSymName, Type));
return false;
}
bool COFFAsmParser::ParseSectionName(StringRef &SectionName) {
if (!getLexer().is(AsmToken::Identifier))
return true;
SectionName = getTok().getIdentifier();
Lex();
return false;
}
// .section name [, "flags"] [, identifier [ identifier ], identifier]
//
// Supported flags:
// a: Ignored.
// b: BSS section (uninitialized data)
// d: data section (initialized data)
// n: "noload" section (removed by linker)
// D: Discardable section
// r: Readable section
// s: Shared section
// w: Writable section
// x: Executable section
// y: Not-readable section (clears 'r')
//
// Subsections are not supported.
bool COFFAsmParser::ParseDirectiveSection(StringRef, SMLoc) {
StringRef SectionName;
if (ParseSectionName(SectionName))
return TokError("expected identifier in directive");
unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ |
COFF::IMAGE_SCN_MEM_WRITE;
if (getLexer().is(AsmToken::Comma)) {
Lex();
if (getLexer().isNot(AsmToken::String))
return TokError("expected string in directive");
StringRef FlagsStr = getTok().getStringContents();
Lex();
if (ParseSectionFlags(SectionName, FlagsStr, &Flags))
return true;
}
COFF::COMDATType Type = (COFF::COMDATType)0;
StringRef COMDATSymName;
if (getLexer().is(AsmToken::Comma)) {
Type = COFF::IMAGE_COMDAT_SELECT_ANY;
Lex();
Flags |= COFF::IMAGE_SCN_LNK_COMDAT;
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected comdat type such as 'discard' or 'largest' "
"after protection bits");
if (parseCOMDATType(Type))
return true;
if (getLexer().isNot(AsmToken::Comma))
return TokError("expected comma in directive");
Lex();
if (getParser().parseIdentifier(COMDATSymName))
return TokError("expected identifier in directive");
}
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
SectionKind Kind = computeSectionKind(Flags);
if (Kind.isText()) {
const Triple &T = getContext().getObjectFileInfo()->getTargetTriple();
if (T.getArch() == Triple::arm || T.getArch() == Triple::thumb)
Flags |= COFF::IMAGE_SCN_MEM_16BIT;
}
ParseSectionSwitch(SectionName, Flags, Kind, COMDATSymName, Type);
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::ParseDirectiveSecRel32(StringRef, SMLoc) {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
return TokError("expected identifier in directive");
int64_t Offset = 0;
SMLoc OffsetLoc;
if (getLexer().is(AsmToken::Plus)) {
OffsetLoc = getLexer().getLoc();
if (getParser().parseAbsoluteExpression(Offset))
return true;
}
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
if (Offset < 0 || Offset > std::numeric_limits<uint32_t>::max())
return Error(
OffsetLoc,
"invalid '.secrel32' directive offset, can't be less "
"than zero or greater than std::numeric_limits<uint32_t>::max()");
MCSymbol *Symbol = getContext().getOrCreateSymbol(SymbolID);
Lex();
getStreamer().EmitCOFFSecRel32(Symbol, Offset);
return false;
}
bool COFFAsmParser::ParseDirectiveRVA(StringRef, SMLoc) {
auto parseOp = [&]() -> bool {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
return TokError("expected identifier in directive");
int64_t Offset = 0;
SMLoc OffsetLoc;
if (getLexer().is(AsmToken::Plus) || getLexer().is(AsmToken::Minus)) {
OffsetLoc = getLexer().getLoc();
if (getParser().parseAbsoluteExpression(Offset))
return true;
}
if (Offset < std::numeric_limits<int32_t>::min() ||
Offset > std::numeric_limits<int32_t>::max())
return Error(OffsetLoc, "invalid '.rva' directive offset, can't be less "
"than -2147483648 or greater than "
"2147483647");
MCSymbol *Symbol = getContext().getOrCreateSymbol(SymbolID);
getStreamer().EmitCOFFImgRel32(Symbol, Offset);
return false;
};
if (getParser().parseMany(parseOp))
return addErrorSuffix(" in directive");
return false;
}
bool COFFAsmParser::ParseDirectiveSafeSEH(StringRef, SMLoc) {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
return TokError("expected identifier in directive");
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
MCSymbol *Symbol = getContext().getOrCreateSymbol(SymbolID);
Lex();
getStreamer().EmitCOFFSafeSEH(Symbol);
return false;
}
bool COFFAsmParser::ParseDirectiveSecIdx(StringRef, SMLoc) {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
return TokError("expected identifier in directive");
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
MCSymbol *Symbol = getContext().getOrCreateSymbol(SymbolID);
Lex();
getStreamer().EmitCOFFSectionIndex(Symbol);
return false;
}
bool COFFAsmParser::ParseDirectiveSymIdx(StringRef, SMLoc) {
StringRef SymbolID;
if (getParser().parseIdentifier(SymbolID))
return TokError("expected identifier in directive");
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
MCSymbol *Symbol = getContext().getOrCreateSymbol(SymbolID);
Lex();
getStreamer().EmitCOFFSymbolIndex(Symbol);
return false;
}
/// ::= [ identifier ]
bool COFFAsmParser::parseCOMDATType(COFF::COMDATType &Type) {
StringRef TypeId = getTok().getIdentifier();
Type = StringSwitch<COFF::COMDATType>(TypeId)
.Case("one_only", COFF::IMAGE_COMDAT_SELECT_NODUPLICATES)
.Case("discard", COFF::IMAGE_COMDAT_SELECT_ANY)
.Case("same_size", COFF::IMAGE_COMDAT_SELECT_SAME_SIZE)
.Case("same_contents", COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH)
.Case("associative", COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
.Case("largest", COFF::IMAGE_COMDAT_SELECT_LARGEST)
.Case("newest", COFF::IMAGE_COMDAT_SELECT_NEWEST)
.Default((COFF::COMDATType)0);
if (Type == 0)
return TokError(Twine("unrecognized COMDAT type '" + TypeId + "'"));
Lex();
return false;
}
/// ParseDirectiveLinkOnce
/// ::= .linkonce [ identifier ]
bool COFFAsmParser::ParseDirectiveLinkOnce(StringRef, SMLoc Loc) {
COFF::COMDATType Type = COFF::IMAGE_COMDAT_SELECT_ANY;
if (getLexer().is(AsmToken::Identifier))
if (parseCOMDATType(Type))
return true;
const MCSectionCOFF *Current =
static_cast<const MCSectionCOFF *>(getStreamer().getCurrentSectionOnly());
if (Type == COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
return Error(Loc, "cannot make section associative with .linkonce");
if (Current->getCharacteristics() & COFF::IMAGE_SCN_LNK_COMDAT)
return Error(Loc, Twine("section '") + Current->getSectionName() +
"' is already linkonce");
Current->setSelection(Type);
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
return false;
}
bool COFFAsmParser::ParseSEHDirectiveStartProc(StringRef, SMLoc Loc) {
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().EmitWinCFIStartProc(Symbol, Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveEndProc(StringRef, SMLoc Loc) {
Lex();
getStreamer().EmitWinCFIEndProc(Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveStartChained(StringRef, SMLoc Loc) {
Lex();
getStreamer().EmitWinCFIStartChained(Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveEndChained(StringRef, SMLoc Loc) {
Lex();
getStreamer().EmitWinCFIEndChained(Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveHandler(StringRef, SMLoc Loc) {
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().EmitWinEHHandler(handler, unwind, except, Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveHandlerData(StringRef, SMLoc Loc) {
Lex();
getStreamer().EmitWinEHHandlerData();
return false;
}
bool COFFAsmParser::ParseSEHDirectiveAllocStack(StringRef, SMLoc Loc) {
int64_t Size;
if (getParser().parseAbsoluteExpression(Size))
return true;
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in directive");
Lex();
getStreamer().EmitWinCFIAllocStack(Size, Loc);
return false;
}
bool COFFAsmParser::ParseSEHDirectiveEndProlog(StringRef, SMLoc Loc) {
Lex();
getStreamer().EmitWinCFIEndProlog(Loc);
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;
}
namespace llvm {
MCAsmParserExtension *createCOFFAsmParser() {
return new COFFAsmParser;
}
} // end namespace llvm