1//===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/ADT/StringRef.h"
10#include "llvm/ADT/Twine.h"
11#include "llvm/BinaryFormat/COFF.h"
12#include "llvm/MC/MCAsmMacro.h"
13#include "llvm/MC/MCContext.h"
14#include "llvm/MC/MCParser/MCAsmLexer.h"
15#include "llvm/MC/MCParser/MCAsmParserExtension.h"
16#include "llvm/MC/MCParser/MCTargetAsmParser.h"
17#include "llvm/MC/MCSectionCOFF.h"
18#include "llvm/MC/MCStreamer.h"
19#include "llvm/MC/MCSymbolCOFF.h"
20#include "llvm/MC/SectionKind.h"
21#include "llvm/Support/Casting.h"
22#include "llvm/Support/SMLoc.h"
23#include <cstdint>
24#include <utility>
25
26using namespace llvm;
27
28namespace {
29
30class COFFMasmParser : public MCAsmParserExtension {
31 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
32 void addDirectiveHandler(StringRef Directive) {
33 MCAsmParser::ExtensionDirectiveHandler Handler =
34 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
35 getParser().addDirectiveHandler(Directive, Handler);
36 }
37
38 bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics);
39
40 bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,
41 StringRef COMDATSymName, COFF::COMDATType Type,
42 Align Alignment);
43
44 bool ParseDirectiveProc(StringRef, SMLoc);
45 bool ParseDirectiveEndProc(StringRef, SMLoc);
46 bool ParseDirectiveSegment(StringRef, SMLoc);
47 bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
48 bool ParseDirectiveIncludelib(StringRef, SMLoc);
49 bool ParseDirectiveOption(StringRef, SMLoc);
50
51 bool ParseDirectiveAlias(StringRef, SMLoc);
52
53 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
54 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
55
56 bool IgnoreDirective(StringRef, SMLoc) {
57 while (!getLexer().is(K: AsmToken::EndOfStatement)) {
58 Lex();
59 }
60 return false;
61 }
62
63 void Initialize(MCAsmParser &Parser) override {
64 // Call the base implementation.
65 MCAsmParserExtension::Initialize(Parser);
66
67 // x64 directives
68 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
69 Directive: ".allocstack");
70 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
71 Directive: ".endprolog");
72
73 // Code label directives
74 // label
75 // org
76
77 // Conditional control flow directives
78 // .break
79 // .continue
80 // .else
81 // .elseif
82 // .endif
83 // .endw
84 // .if
85 // .repeat
86 // .until
87 // .untilcxz
88 // .while
89
90 // Data allocation directives
91 // align
92 // even
93 // mmword
94 // tbyte
95 // xmmword
96 // ymmword
97
98 // Listing control directives
99 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".cref");
100 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".list");
101 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".listall");
102 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".listif");
103 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".listmacro");
104 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".listmacroall");
105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".nocref");
106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".nolist");
107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".nolistif");
108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".nolistmacro");
109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: "page");
110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: "subtitle");
111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".tfcond");
112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: "title");
113
114 // Macro directives
115 // goto
116
117 // Miscellaneous directives
118 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>(Directive: "alias");
119 // assume
120 // .fpo
121 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
122 Directive: "includelib");
123 addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>(Directive: "option");
124 // popcontext
125 // pushcontext
126 // .safeseh
127
128 // Procedure directives
129 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>(Directive: "endp");
130 // invoke (32-bit only)
131 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>(Directive: "proc");
132 // proto
133
134 // Processor directives; all ignored
135 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".386");
136 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".386p");
137 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".387");
138 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".486");
139 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".486p");
140 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".586");
141 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".586p");
142 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".686");
143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".686p");
144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".k3d");
145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".mmx");
146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".xmm");
147
148 // Scope directives
149 // comm
150 // externdef
151
152 // Segment directives
153 // .alpha (32-bit only, order segments alphabetically)
154 // .dosseg (32-bit only, order segments in DOS convention)
155 // .seq (32-bit only, order segments sequentially)
156 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>(Directive: "ends");
157 // group (32-bit only)
158 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>(Directive: "segment");
159
160 // Simplified segment directives
161 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(Directive: ".code");
162 // .const
163 addDirectiveHandler<
164 &COFFMasmParser::ParseSectionDirectiveInitializedData>(Directive: ".data");
165 addDirectiveHandler<
166 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(Directive: ".data?");
167 // .exit
168 // .fardata
169 // .fardata?
170 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(Directive: ".model");
171 // .stack
172 // .startup
173
174 // String directives, written <name> <directive> <params>
175 // catstr (equivalent to <name> TEXTEQU <params>)
176 // instr (equivalent to <name> = @InStr(<params>))
177 // sizestr (equivalent to <name> = @SizeStr(<params>))
178 // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
179
180 // Structure and record directives
181 // record
182 // typedef
183 }
184
185 bool ParseSectionDirectiveCode(StringRef, SMLoc) {
186 return ParseSectionSwitch(SectionName: ".text", Characteristics: COFF::IMAGE_SCN_CNT_CODE |
187 COFF::IMAGE_SCN_MEM_EXECUTE |
188 COFF::IMAGE_SCN_MEM_READ);
189 }
190
191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
192 return ParseSectionSwitch(SectionName: ".data", Characteristics: COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
193 COFF::IMAGE_SCN_MEM_READ |
194 COFF::IMAGE_SCN_MEM_WRITE);
195 }
196
197 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
198 return ParseSectionSwitch(SectionName: ".bss", Characteristics: COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |
199 COFF::IMAGE_SCN_MEM_READ |
200 COFF::IMAGE_SCN_MEM_WRITE);
201 }
202
203 /// Stack of active procedure definitions.
204 SmallVector<StringRef, 1> CurrentProcedures;
205 SmallVector<bool, 1> CurrentProceduresFramed;
206
207public:
208 COFFMasmParser() = default;
209};
210
211} // end anonymous namespace.
212
213bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
214 unsigned Characteristics) {
215 return ParseSectionSwitch(SectionName, Characteristics, COMDATSymName: "",
216 Type: (COFF::COMDATType)0, Alignment: Align(16));
217}
218
219bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
220 unsigned Characteristics,
221 StringRef COMDATSymName,
222 COFF::COMDATType Type,
223 Align Alignment) {
224 if (getLexer().isNot(K: AsmToken::EndOfStatement))
225 return TokError(Msg: "unexpected token in section switching directive");
226 Lex();
227
228 MCSection *Section = getContext().getCOFFSection(Section: SectionName, Characteristics,
229 COMDATSymName, Selection: Type);
230 Section->setAlignment(Alignment);
231 getStreamer().switchSection(Section);
232
233 return false;
234}
235
236bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
237 StringRef SegmentName;
238 if (!getLexer().is(K: AsmToken::Identifier))
239 return TokError(Msg: "expected identifier in directive");
240 SegmentName = getTok().getIdentifier();
241 Lex();
242
243 StringRef SectionName = SegmentName;
244 SmallVector<char, 247> SectionNameVector;
245
246 StringRef Class;
247 if (SegmentName == "_TEXT" || SegmentName.starts_with(Prefix: "_TEXT$")) {
248 if (SegmentName.size() == 5) {
249 SectionName = ".text";
250 } else {
251 SectionName =
252 (".text$" + SegmentName.substr(Start: 6)).toStringRef(Out&: SectionNameVector);
253 }
254 Class = "CODE";
255 }
256
257 // Parse all options to end of statement.
258 // Alignment defaults to PARA if unspecified.
259 int64_t Alignment = 16;
260 // Default flags are used only if no characteristics are set.
261 bool DefaultCharacteristics = true;
262 unsigned Flags = 0;
263 // "obsolete" according to the documentation, but still supported.
264 bool Readonly = false;
265 while (getLexer().isNot(K: AsmToken::EndOfStatement)) {
266 switch (getTok().getKind()) {
267 default:
268 break;
269 case AsmToken::String: {
270 // Class identifier; overrides Kind.
271 Class = getTok().getStringContents();
272 Lex();
273 break;
274 }
275 case AsmToken::Identifier: {
276 SMLoc KeywordLoc = getTok().getLoc();
277 StringRef Keyword;
278 if (getParser().parseIdentifier(Res&: Keyword)) {
279 llvm_unreachable("failed to parse identifier at an identifier token");
280 }
281 if (Keyword.equals_insensitive(RHS: "byte")) {
282 Alignment = 1;
283 } else if (Keyword.equals_insensitive(RHS: "word")) {
284 Alignment = 2;
285 } else if (Keyword.equals_insensitive(RHS: "dword")) {
286 Alignment = 4;
287 } else if (Keyword.equals_insensitive(RHS: "para")) {
288 Alignment = 16;
289 } else if (Keyword.equals_insensitive(RHS: "page")) {
290 Alignment = 256;
291 } else if (Keyword.equals_insensitive(RHS: "align")) {
292 if (getParser().parseToken(T: AsmToken::LParen) ||
293 getParser().parseIntToken(V&: Alignment,
294 ErrMsg: "Expected integer alignment") ||
295 getParser().parseToken(T: AsmToken::RParen)) {
296 return Error(L: getTok().getLoc(),
297 Msg: "Expected (n) following ALIGN in SEGMENT directive");
298 }
299 if (!isPowerOf2_64(Value: Alignment) || Alignment > 8192) {
300 return Error(L: KeywordLoc,
301 Msg: "ALIGN argument must be a power of 2 from 1 to 8192");
302 }
303 } else if (Keyword.equals_insensitive(RHS: "alias")) {
304 if (getParser().parseToken(T: AsmToken::LParen) ||
305 !getTok().is(K: AsmToken::String))
306 return Error(
307 L: getTok().getLoc(),
308 Msg: "Expected (string) following ALIAS in SEGMENT directive");
309 SectionName = getTok().getStringContents();
310 Lex();
311 if (getParser().parseToken(T: AsmToken::RParen))
312 return Error(
313 L: getTok().getLoc(),
314 Msg: "Expected (string) following ALIAS in SEGMENT directive");
315 } else if (Keyword.equals_insensitive(RHS: "readonly")) {
316 Readonly = true;
317 } else {
318 unsigned Characteristic =
319 StringSwitch<unsigned>(Keyword)
320 .CaseLower(S: "info", Value: COFF::IMAGE_SCN_LNK_INFO)
321 .CaseLower(S: "read", Value: COFF::IMAGE_SCN_MEM_READ)
322 .CaseLower(S: "write", Value: COFF::IMAGE_SCN_MEM_WRITE)
323 .CaseLower(S: "execute", Value: COFF::IMAGE_SCN_MEM_EXECUTE)
324 .CaseLower(S: "shared", Value: COFF::IMAGE_SCN_MEM_SHARED)
325 .CaseLower(S: "nopage", Value: COFF::IMAGE_SCN_MEM_NOT_PAGED)
326 .CaseLower(S: "nocache", Value: COFF::IMAGE_SCN_MEM_NOT_CACHED)
327 .CaseLower(S: "discard", Value: COFF::IMAGE_SCN_MEM_DISCARDABLE)
328 .Default(Value: -1);
329 if (Characteristic == static_cast<unsigned>(-1)) {
330 return Error(L: KeywordLoc,
331 Msg: "Expected characteristic in SEGMENT directive; found '" +
332 Keyword + "'");
333 }
334 Flags |= Characteristic;
335 DefaultCharacteristics = false;
336 }
337 }
338 }
339 }
340
341 SectionKind Kind = StringSwitch<SectionKind>(Class)
342 .CaseLower(S: "data", Value: SectionKind::getData())
343 .CaseLower(S: "code", Value: SectionKind::getText())
344 .CaseLower(S: "const", Value: SectionKind::getReadOnly())
345 .Default(Value: SectionKind::getData());
346 if (Kind.isText()) {
347 if (DefaultCharacteristics) {
348 Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;
349 }
350 Flags |= COFF::IMAGE_SCN_CNT_CODE;
351 } else {
352 if (DefaultCharacteristics) {
353 Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
354 }
355 Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
356 }
357 if (Readonly) {
358 Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;
359 }
360
361 MCSection *Section = getContext().getCOFFSection(Section: SectionName, Characteristics: Flags, COMDATSymName: "",
362 Selection: (COFF::COMDATType)(0));
363 if (Alignment != 0) {
364 Section->setAlignment(Align(Alignment));
365 }
366 getStreamer().switchSection(Section);
367 return false;
368}
369
370/// ParseDirectiveSegmentEnd
371/// ::= identifier "ends"
372bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
373 StringRef SegmentName;
374 if (!getLexer().is(K: AsmToken::Identifier))
375 return TokError(Msg: "expected identifier in directive");
376 SegmentName = getTok().getIdentifier();
377
378 // Ignore; no action necessary.
379 Lex();
380 return false;
381}
382
383/// ParseDirectiveIncludelib
384/// ::= "includelib" identifier
385bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
386 StringRef Lib;
387 if (getParser().parseIdentifier(Res&: Lib))
388 return TokError(Msg: "expected identifier in includelib directive");
389
390 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
391 getStreamer().pushSection();
392 getStreamer().switchSection(Section: getContext().getCOFFSection(
393 Section: ".drectve", Characteristics: Flags, COMDATSymName: "", Selection: (COFF::COMDATType)(0)));
394 getStreamer().emitBytes(Data: "/DEFAULTLIB:");
395 getStreamer().emitBytes(Data: Lib);
396 getStreamer().emitBytes(Data: " ");
397 getStreamer().popSection();
398 return false;
399}
400
401/// ParseDirectiveOption
402/// ::= "option" option-list
403bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {
404 auto parseOption = [&]() -> bool {
405 StringRef Option;
406 if (getParser().parseIdentifier(Res&: Option))
407 return TokError(Msg: "expected identifier for option name");
408 if (Option.equals_insensitive(RHS: "prologue")) {
409 StringRef MacroId;
410 if (parseToken(T: AsmToken::Colon) || getParser().parseIdentifier(Res&: MacroId))
411 return TokError(Msg: "expected :macroId after OPTION PROLOGUE");
412 if (MacroId.equals_insensitive(RHS: "none")) {
413 // Since we currently don't implement prologues/epilogues, NONE is our
414 // default.
415 return false;
416 }
417 return TokError(Msg: "OPTION PROLOGUE is currently unsupported");
418 }
419 if (Option.equals_insensitive(RHS: "epilogue")) {
420 StringRef MacroId;
421 if (parseToken(T: AsmToken::Colon) || getParser().parseIdentifier(Res&: MacroId))
422 return TokError(Msg: "expected :macroId after OPTION EPILOGUE");
423 if (MacroId.equals_insensitive(RHS: "none")) {
424 // Since we currently don't implement prologues/epilogues, NONE is our
425 // default.
426 return false;
427 }
428 return TokError(Msg: "OPTION EPILOGUE is currently unsupported");
429 }
430 return TokError(Msg: "OPTION '" + Option + "' is currently unsupported");
431 };
432
433 if (parseMany(parseOne: parseOption))
434 return addErrorSuffix(Suffix: " in OPTION directive");
435 return false;
436}
437
438/// ParseDirectiveProc
439/// TODO(epastor): Implement parameters and other attributes.
440/// ::= label "proc" [[distance]]
441/// statements
442/// label "endproc"
443bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
444 StringRef Label;
445 if (getParser().parseIdentifier(Res&: Label))
446 return Error(L: Loc, Msg: "expected identifier for procedure");
447 if (getLexer().is(K: AsmToken::Identifier)) {
448 StringRef nextVal = getTok().getString();
449 SMLoc nextLoc = getTok().getLoc();
450 if (nextVal.equals_insensitive(RHS: "far")) {
451 // TODO(epastor): Handle far procedure definitions.
452 Lex();
453 return Error(L: nextLoc, Msg: "far procedure definitions not yet supported");
454 } else if (nextVal.equals_insensitive(RHS: "near")) {
455 Lex();
456 nextVal = getTok().getString();
457 nextLoc = getTok().getLoc();
458 }
459 }
460 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(Val: getContext().getOrCreateSymbol(Name: Label));
461
462 // Define symbol as simple external function
463 Sym->setExternal(true);
464 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
465
466 bool Framed = false;
467 if (getLexer().is(K: AsmToken::Identifier) &&
468 getTok().getString().equals_insensitive(RHS: "frame")) {
469 Lex();
470 Framed = true;
471 getStreamer().emitWinCFIStartProc(Symbol: Sym, Loc);
472 }
473 getStreamer().emitLabel(Symbol: Sym, Loc);
474
475 CurrentProcedures.push_back(Elt: Label);
476 CurrentProceduresFramed.push_back(Elt: Framed);
477 return false;
478}
479bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
480 StringRef Label;
481 SMLoc LabelLoc = getTok().getLoc();
482 if (getParser().parseIdentifier(Res&: Label))
483 return Error(L: LabelLoc, Msg: "expected identifier for procedure end");
484
485 if (CurrentProcedures.empty())
486 return Error(L: Loc, Msg: "endp outside of procedure block");
487 else if (!CurrentProcedures.back().equals_insensitive(RHS: Label))
488 return Error(L: LabelLoc, Msg: "endp does not match current procedure '" +
489 CurrentProcedures.back() + "'");
490
491 if (CurrentProceduresFramed.back()) {
492 getStreamer().emitWinCFIEndProc(Loc);
493 }
494 CurrentProcedures.pop_back();
495 CurrentProceduresFramed.pop_back();
496 return false;
497}
498
499bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
500 std::string AliasName, ActualName;
501 if (getTok().isNot(K: AsmToken::Less) ||
502 getParser().parseAngleBracketString(Data&: AliasName))
503 return Error(L: getTok().getLoc(), Msg: "expected <aliasName>");
504 if (getParser().parseToken(T: AsmToken::Equal))
505 return addErrorSuffix(Suffix: " in " + Directive + " directive");
506 if (getTok().isNot(K: AsmToken::Less) ||
507 getParser().parseAngleBracketString(Data&: ActualName))
508 return Error(L: getTok().getLoc(), Msg: "expected <actualName>");
509
510 MCSymbol *Alias = getContext().getOrCreateSymbol(Name: AliasName);
511 MCSymbol *Actual = getContext().getOrCreateSymbol(Name: ActualName);
512
513 getStreamer().emitWeakReference(Alias, Symbol: Actual);
514
515 return false;
516}
517
518bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
519 SMLoc Loc) {
520 int64_t Size;
521 SMLoc SizeLoc = getTok().getLoc();
522 if (getParser().parseAbsoluteExpression(Res&: Size))
523 return Error(L: SizeLoc, Msg: "expected integer size");
524 if (Size % 8 != 0)
525 return Error(L: SizeLoc, Msg: "stack size must be a multiple of 8");
526 getStreamer().emitWinCFIAllocStack(Size: static_cast<unsigned>(Size), Loc);
527 return false;
528}
529
530bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
531 SMLoc Loc) {
532 getStreamer().emitWinCFIEndProlog(Loc);
533 return false;
534}
535
536namespace llvm {
537
538MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
539
540} // end namespace llvm
541