#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCAsmParser.h"
#include "llvm/MC/MCParser/MCAsmParserExtension.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCSectionWasm.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbolWasm.h"
#include "llvm/Support/Casting.h"
using namespace llvm;
namespace {
class WasmAsmParser : public MCAsmParserExtension {
MCAsmParser *Parser = nullptr;
MCAsmLexer *Lexer = nullptr;
template<bool (WasmAsmParser::*HandlerMethod)(StringRef, SMLoc)>
void addDirectiveHandler(StringRef Directive) {
MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair(
this, HandleDirective<WasmAsmParser, HandlerMethod>);
getParser().addDirectiveHandler(Directive, Handler);
}
public:
WasmAsmParser() { BracketExpressionsSupported = true; }
void Initialize(MCAsmParser &P) override {
Parser = &P;
Lexer = &Parser->getLexer();
this->MCAsmParserExtension::Initialize(*Parser);
addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveText>(".text");
addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveData>(".data");
addDirectiveHandler<&WasmAsmParser::parseSectionDirective>(".section");
addDirectiveHandler<&WasmAsmParser::parseDirectiveSize>(".size");
addDirectiveHandler<&WasmAsmParser::parseDirectiveType>(".type");
addDirectiveHandler<&WasmAsmParser::ParseDirectiveIdent>(".ident");
addDirectiveHandler<
&WasmAsmParser::ParseDirectiveSymbolAttribute>(".weak");
addDirectiveHandler<
&WasmAsmParser::ParseDirectiveSymbolAttribute>(".local");
addDirectiveHandler<
&WasmAsmParser::ParseDirectiveSymbolAttribute>(".internal");
addDirectiveHandler<
&WasmAsmParser::ParseDirectiveSymbolAttribute>(".hidden");
}
bool error(const StringRef &Msg, const AsmToken &Tok) {
return Parser->Error(Tok.getLoc(), Msg + Tok.getString());
}
bool isNext(AsmToken::TokenKind Kind) {
auto Ok = Lexer->is(Kind);
if (Ok)
Lex();
return Ok;
}
bool expect(AsmToken::TokenKind Kind, const char *KindName) {
if (!isNext(Kind))
return error(std::string("Expected ") + KindName + ", instead got: ",
Lexer->getTok());
return false;
}
bool parseSectionDirectiveText(StringRef, SMLoc) {
return false;
}
bool parseSectionDirectiveData(StringRef, SMLoc) {
auto *S = getContext().getObjectFileInfo()->getDataSection();
getStreamer().switchSection(S);
return false;
}
uint32_t parseSectionFlags(StringRef FlagStr, bool &Passive, bool &Group) {
uint32_t flags = 0;
for (char C : FlagStr) {
switch (C) {
case 'p':
Passive = true;
break;
case 'G':
Group = true;
break;
case 'T':
flags |= wasm::WASM_SEG_FLAG_TLS;
break;
case 'S':
flags |= wasm::WASM_SEG_FLAG_STRINGS;
break;
default:
return -1U;
}
}
return flags;
}
bool parseGroup(StringRef &GroupName) {
if (Lexer->isNot(AsmToken::Comma))
return TokError("expected group name");
Lex();
if (Lexer->is(AsmToken::Integer)) {
GroupName = getTok().getString();
Lex();
} else if (Parser->parseIdentifier(GroupName)) {
return TokError("invalid group name");
}
if (Lexer->is(AsmToken::Comma)) {
Lex();
StringRef Linkage;
if (Parser->parseIdentifier(Linkage))
return TokError("invalid linkage");
if (Linkage != "comdat")
return TokError("Linkage must be 'comdat'");
}
return false;
}
bool parseSectionDirective(StringRef, SMLoc loc) {
StringRef Name;
if (Parser->parseIdentifier(Name))
return TokError("expected identifier in directive");
if (expect(AsmToken::Comma, ","))
return true;
if (Lexer->isNot(AsmToken::String))
return error("expected string in directive, instead got: ", Lexer->getTok());
auto Kind = StringSwitch<Optional<SectionKind>>(Name)
.StartsWith(".data", SectionKind::getData())
.StartsWith(".tdata", SectionKind::getThreadData())
.StartsWith(".tbss", SectionKind::getThreadBSS())
.StartsWith(".rodata", SectionKind::getReadOnly())
.StartsWith(".text", SectionKind::getText())
.StartsWith(".custom_section", SectionKind::getMetadata())
.StartsWith(".bss", SectionKind::getBSS())
.StartsWith(".init_array", SectionKind::getData())
.StartsWith(".debug_", SectionKind::getMetadata())
.Default(SectionKind::getData());
bool Passive = false;
bool Group = false;
uint32_t Flags =
parseSectionFlags(getTok().getStringContents(), Passive, Group);
if (Flags == -1U)
return TokError("unknown flag");
Lex();
if (expect(AsmToken::Comma, ",") || expect(AsmToken::At, "@"))
return true;
StringRef GroupName;
if (Group && parseGroup(GroupName))
return true;
if (expect(AsmToken::EndOfStatement, "eol"))
return true;
MCSectionWasm *WS = getContext().getWasmSection(
Name, *Kind, Flags, GroupName, MCContext::GenericSectionID);
if (WS->getSegmentFlags() != Flags)
Parser->Error(loc, "changed section flags for " + Name +
", expected: 0x" +
utohexstr(WS->getSegmentFlags()));
if (Passive) {
if (!WS->isWasmData())
return Parser->Error(loc, "Only data sections can be passive");
WS->setPassive();
}
getStreamer().switchSection(WS);
return false;
}
bool parseDirectiveSize(StringRef, SMLoc) {
StringRef Name;
if (Parser->parseIdentifier(Name))
return TokError("expected identifier in directive");
auto Sym = getContext().getOrCreateSymbol(Name);
if (expect(AsmToken::Comma, ","))
return true;
const MCExpr *Expr;
if (Parser->parseExpression(Expr))
return true;
if (expect(AsmToken::EndOfStatement, "eol"))
return true;
getStreamer().emitELFSize(Sym, Expr);
return false;
}
bool parseDirectiveType(StringRef, SMLoc) {
if (!Lexer->is(AsmToken::Identifier))
return error("Expected label after .type directive, got: ",
Lexer->getTok());
auto WasmSym = cast<MCSymbolWasm>(
getStreamer().getContext().getOrCreateSymbol(
Lexer->getTok().getString()));
Lex();
if (!(isNext(AsmToken::Comma) && isNext(AsmToken::At) &&
Lexer->is(AsmToken::Identifier)))
return error("Expected label,@type declaration, got: ", Lexer->getTok());
auto TypeName = Lexer->getTok().getString();
if (TypeName == "function") {
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
auto *Current =
cast<MCSectionWasm>(getStreamer().getCurrentSection().first);
if (Current->getGroup())
WasmSym->setComdat(true);
} else if (TypeName == "global")
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
else if (TypeName == "object")
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA);
else
return error("Unknown WASM symbol type: ", Lexer->getTok());
Lex();
return expect(AsmToken::EndOfStatement, "EOL");
}
bool ParseDirectiveIdent(StringRef, SMLoc) {
if (getLexer().isNot(AsmToken::String))
return TokError("unexpected token in '.ident' directive");
StringRef Data = getTok().getIdentifier();
Lex();
if (getLexer().isNot(AsmToken::EndOfStatement))
return TokError("unexpected token in '.ident' directive");
Lex();
getStreamer().emitIdent(Data);
return false;
}
bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) {
MCSymbolAttr Attr = StringSwitch<MCSymbolAttr>(Directive)
.Case(".weak", MCSA_Weak)
.Case(".local", MCSA_Local)
.Case(".hidden", MCSA_Hidden)
.Case(".internal", MCSA_Internal)
.Case(".protected", MCSA_Protected)
.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;
}
};
}
namespace llvm {
MCAsmParserExtension *createWasmAsmParser() {
return new WasmAsmParser;
}
}