1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 20:23:11 +01:00
llvm-mirror/lib/MC/MCParser/COFFMasmParser.cpp
Eric Astor ca668acee6 [ms] [llvm-ml] Add a draft MASM parser
Summary:
Many directives are unavailable, and support for others may be limited.

This first draft has preliminary support for:
    - conditional directives (including errors),
    - data allocation (unsigned types up to 8 bytes, and ALIGN),
    - equates/variables (numeric and text),
    - and procedure directives (without parameters),
as well as COMMENT, ECHO, INCLUDE, INCLUDELIB, PUBLIC, and EXTERN. Text variables (aka text macros) are expanded in-place wherever the identifier occurs.

We deliberately ignore all ml.exe processor directives.

Prominent features not yet supported:
    - structs
    - macros (both procedures and functions)
    - procedures (with specified parameters)
    - substitution & expansion operators

Conditional directives are complicated by the fact that "ifdef rax" is a valid way to check if a file is being assembled for a 64-bit x86 processor; we add support for "ifdef <register>" in general, which requires adding a tryParseRegister method to all MCTargetAsmParsers. (Some targets require backtracking in the non-register case.)

Reviewers: rnk, thakis

Reviewed By: thakis

Subscribers: kerbowa, merge_guards_bot, wuzish, arsenm, dschuff, jyknight, dylanmckay, sdardis, nemanjai, jvesely, mgorny, sbc100, jgravelle-google, hiraditya, aheejin, kbarton, fedor.sergeev, asb, rbar, johnrusso, simoncook, sabuasal, niosHD, jrtc27, MaskRay, zzheng, edward-jones, atanasyan, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, jsji, Jim, s.egerton, pzheng, sameer.abuasal, apazos, luismarques, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D72680
2020-02-16 12:30:46 -05:00

387 lines
13 KiB
C++

//===- COFFMasmParser.cpp - COFF MASM 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/MCAsmParserUtils.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 COFFMasmParser : public MCAsmParserExtension {
template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
void addDirectiveHandler(StringRef Directive) {
MCAsmParser::ExtensionDirectiveHandler Handler =
std::make_pair(this, HandleDirective<COFFMasmParser, 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 ParseDirectiveProc(StringRef, SMLoc);
bool ParseDirectiveEndProc(StringRef, SMLoc);
bool ParseDirectiveSegment(StringRef, SMLoc);
bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
bool ParseDirectiveIncludelib(StringRef, SMLoc);
bool IgnoreDirective(StringRef, SMLoc) {
while (!getLexer().is(AsmToken::EndOfStatement)) {
Lex();
}
return false;
}
void Initialize(MCAsmParser &Parser) override {
// Call the base implementation.
MCAsmParserExtension::Initialize(Parser);
// x64 directives
// .allocstack
// .endprolog
// .pushframe
// .pushreg
// .savereg
// .savexmm128
// .setframe
// Code label directives
// label
// org
// Conditional control flow directives
// .break
// .continue
// .else
// .elseif
// .endif
// .endw
// .if
// .repeat
// .until
// .untilcxz
// .while
// Data allocation directives
// align
// byte/sbyte
// dword/sdword
// even
// fword
// qword
// real4
// real8
// real10
// tbyte
// word/sword
// Listing control directives
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
// Macro directives
// endm
// exitm
// goto
// local
// macro
// purge
// Miscellaneous directives
// alias
// assume
// .fpo
addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
"includelib");
// mmword
// option
// popcontext
// pushcontext
// .radix
// .safeseh
// xmmword
// ymmword
// Procedure directives
addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
// invoke (32-bit only)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
// proto
// Processor directives
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
// Repeat blocks directives
// for
// forc
// goto
// repeat
// while
// Scope directives
// comm
// externdef
// Segment directives
// .alpha (32-bit only, order segments alphabetically)
// .dosseg (32-bit only, order segments in DOS convention)
// .seq (32-bit only, order segments sequentially)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
// group (32-bit only)
addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
// Simplified segment directives
addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
// .const
addDirectiveHandler<
&COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
addDirectiveHandler<
&COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
// .exit
// .fardata
// .fardata?
addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
// .stack
// .startup
// String directives, written <name> <directive> <params>
// catstr (equivalent to <name> TEXTEQU <params>)
// instr (equivalent to <name> = @InStr(<params>))
// sizestr (equivalent to <name> = @SizeStr(<params>))
// substr (equivalent to <name> TEXTEQU @SubStr(<params>))
// Structure and record directives
// ends
// record
// struct
// typedef
// union
}
bool ParseSectionDirectiveCode(StringRef, SMLoc) {
return ParseSectionSwitch(".text",
COFF::IMAGE_SCN_CNT_CODE
| COFF::IMAGE_SCN_MEM_EXECUTE
| COFF::IMAGE_SCN_MEM_READ,
SectionKind::getText());
}
bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
return ParseSectionSwitch(".data",
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE,
SectionKind::getData());
}
bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
return ParseSectionSwitch(".bss",
COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
| COFF::IMAGE_SCN_MEM_READ
| COFF::IMAGE_SCN_MEM_WRITE,
SectionKind::getBSS());
}
StringRef CurrentProcedure;
public:
COFFMasmParser() = 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 COFFMasmParser::ParseSectionSwitch(StringRef Section,
unsigned Characteristics,
SectionKind Kind) {
return ParseSectionSwitch(Section, Characteristics, Kind, "",
(COFF::COMDATType)0);
}
bool COFFMasmParser::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 COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
StringRef SegmentName;
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected identifier in directive");
SegmentName = getTok().getIdentifier();
Lex();
StringRef SectionName = SegmentName;
SmallVector<char, 247> SectionNameVector;
unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
if (SegmentName.size() == 5) {
SectionName = ".text";
} else {
SectionName =
(".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
}
Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE |
COFF::IMAGE_SCN_MEM_READ;
}
SectionKind Kind = computeSectionKind(Flags);
getStreamer().SwitchSection(getContext().getCOFFSection(
SectionName, Flags, Kind, "", (COFF::COMDATType)(0)));
return false;
}
/// ParseDirectiveSegmentEnd
/// ::= identifier "ends"
bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
StringRef SegmentName;
if (!getLexer().is(AsmToken::Identifier))
return TokError("expected identifier in directive");
SegmentName = getTok().getIdentifier();
// Ignore; no action necessary.
Lex();
return false;
}
/// ParseDirectiveIncludelib
/// ::= "includelib" identifier
bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
StringRef Lib;
if (getParser().parseIdentifier(Lib))
return TokError("expected identifier in includelib directive");
unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
SectionKind Kind = computeSectionKind(Flags);
getStreamer().PushSection();
getStreamer().SwitchSection(getContext().getCOFFSection(
".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
getStreamer().emitBytes("/DEFAULTLIB:");
getStreamer().emitBytes(Lib);
getStreamer().emitBytes(" ");
getStreamer().PopSection();
return false;
}
/// ParseDirectiveProc
/// TODO(epastor): Implement parameters and other attributes.
/// ::= label "proc" [[distance]]
/// statements
/// label "endproc"
bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
StringRef Label;
if (getParser().parseIdentifier(Label))
return Error(Loc, "expected identifier for procedure");
if (getLexer().is(AsmToken::Identifier)) {
StringRef nextVal = getTok().getString();
SMLoc nextLoc = getTok().getLoc();
if (nextVal.equals_lower("far")) {
// TODO(epastor): Handle far procedure definitions.
Lex();
return Error(nextLoc, "far procedure definitions not yet supported");
} else if (nextVal.equals_lower("near")) {
Lex();
nextVal = getTok().getString();
nextLoc = getTok().getLoc();
}
}
MCSymbol *Sym = getContext().getOrCreateSymbol(Label);
// Define symbol as simple function
getStreamer().BeginCOFFSymbolDef(Sym);
getStreamer().EmitCOFFSymbolStorageClass(2);
getStreamer().EmitCOFFSymbolType(0x20);
getStreamer().EndCOFFSymbolDef();
getStreamer().emitLabel(Sym, Loc);
CurrentProcedure = Label;
return false;
}
bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
StringRef Label;
SMLoc LabelLoc = getTok().getLoc();
if (getParser().parseIdentifier(Label))
return Error(LabelLoc, "expected identifier for procedure end");
if (CurrentProcedure.empty())
return Error(Loc, "endp outside of procedure block");
else if (CurrentProcedure != Label)
return Error(LabelLoc, "endp does not match current procedure '" +
CurrentProcedure + "'");
return false;
}
namespace llvm {
MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
} // end namespace llvm