1 //===- WasmAsmParser.cpp - Wasm 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 // Note, this is for wasm, the binary format (analogous to ELF), not wasm, 10 // the instruction set (analogous to x86), for which parsing code lives in 11 // WebAssemblyAsmParser. 12 // 13 // This file contains processing for generic directives implemented using 14 // MCTargetStreamer, the ones that depend on WebAssemblyTargetStreamer are in 15 // WebAssemblyAsmParser. 16 // 17 //===----------------------------------------------------------------------===// 18 19 #include "llvm/BinaryFormat/Wasm.h" 20 #include "llvm/MC/MCContext.h" 21 #include "llvm/MC/MCParser/MCAsmLexer.h" 22 #include "llvm/MC/MCParser/MCAsmParser.h" 23 #include "llvm/MC/MCParser/MCAsmParserExtension.h" 24 #include "llvm/MC/MCObjectFileInfo.h" 25 #include "llvm/MC/MCSectionWasm.h" 26 #include "llvm/MC/MCStreamer.h" 27 #include "llvm/MC/MCSymbolWasm.h" 28 #include "llvm/Support/Casting.h" 29 30 using namespace llvm; 31 32 namespace { 33 34 class WasmAsmParser : public MCAsmParserExtension { 35 MCAsmParser *Parser = nullptr; 36 MCAsmLexer *Lexer = nullptr; 37 38 template<bool (WasmAsmParser::*HandlerMethod)(StringRef, SMLoc)> 39 void addDirectiveHandler(StringRef Directive) { 40 MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair( 41 this, HandleDirective<WasmAsmParser, HandlerMethod>); 42 43 getParser().addDirectiveHandler(Directive, Handler); 44 } 45 46 public: 47 WasmAsmParser() { BracketExpressionsSupported = true; } 48 49 void Initialize(MCAsmParser &P) override { 50 Parser = &P; 51 Lexer = &Parser->getLexer(); 52 // Call the base implementation. 53 this->MCAsmParserExtension::Initialize(*Parser); 54 55 addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveText>(".text"); 56 addDirectiveHandler<&WasmAsmParser::parseSectionDirectiveData>(".data"); 57 addDirectiveHandler<&WasmAsmParser::parseSectionDirective>(".section"); 58 addDirectiveHandler<&WasmAsmParser::parseDirectiveSize>(".size"); 59 addDirectiveHandler<&WasmAsmParser::parseDirectiveType>(".type"); 60 addDirectiveHandler<&WasmAsmParser::ParseDirectiveIdent>(".ident"); 61 addDirectiveHandler< 62 &WasmAsmParser::ParseDirectiveSymbolAttribute>(".weak"); 63 addDirectiveHandler< 64 &WasmAsmParser::ParseDirectiveSymbolAttribute>(".local"); 65 addDirectiveHandler< 66 &WasmAsmParser::ParseDirectiveSymbolAttribute>(".internal"); 67 addDirectiveHandler< 68 &WasmAsmParser::ParseDirectiveSymbolAttribute>(".hidden"); 69 } 70 71 bool error(const StringRef &Msg, const AsmToken &Tok) { 72 return Parser->Error(Tok.getLoc(), Msg + Tok.getString()); 73 } 74 75 bool isNext(AsmToken::TokenKind Kind) { 76 auto Ok = Lexer->is(Kind); 77 if (Ok) 78 Lex(); 79 return Ok; 80 } 81 82 bool expect(AsmToken::TokenKind Kind, const char *KindName) { 83 if (!isNext(Kind)) 84 return error(std::string("Expected ") + KindName + ", instead got: ", 85 Lexer->getTok()); 86 return false; 87 } 88 89 bool parseSectionDirectiveText(StringRef, SMLoc) { 90 // FIXME: .text currently no-op. 91 return false; 92 } 93 94 bool parseSectionDirectiveData(StringRef, SMLoc) { 95 auto *S = getContext().getObjectFileInfo()->getDataSection(); 96 getStreamer().switchSection(S); 97 return false; 98 } 99 100 uint32_t parseSectionFlags(StringRef FlagStr, bool &Passive, bool &Group) { 101 uint32_t flags = 0; 102 for (char C : FlagStr) { 103 switch (C) { 104 case 'p': 105 Passive = true; 106 break; 107 case 'G': 108 Group = true; 109 break; 110 case 'T': 111 flags |= wasm::WASM_SEG_FLAG_TLS; 112 break; 113 case 'S': 114 flags |= wasm::WASM_SEG_FLAG_STRINGS; 115 break; 116 default: 117 return -1U; 118 } 119 } 120 return flags; 121 } 122 123 bool parseGroup(StringRef &GroupName) { 124 if (Lexer->isNot(AsmToken::Comma)) 125 return TokError("expected group name"); 126 Lex(); 127 if (Lexer->is(AsmToken::Integer)) { 128 GroupName = getTok().getString(); 129 Lex(); 130 } else if (Parser->parseIdentifier(GroupName)) { 131 return TokError("invalid group name"); 132 } 133 if (Lexer->is(AsmToken::Comma)) { 134 Lex(); 135 StringRef Linkage; 136 if (Parser->parseIdentifier(Linkage)) 137 return TokError("invalid linkage"); 138 if (Linkage != "comdat") 139 return TokError("Linkage must be 'comdat'"); 140 } 141 return false; 142 } 143 144 bool parseSectionDirective(StringRef, SMLoc loc) { 145 StringRef Name; 146 if (Parser->parseIdentifier(Name)) 147 return TokError("expected identifier in directive"); 148 149 if (expect(AsmToken::Comma, ",")) 150 return true; 151 152 if (Lexer->isNot(AsmToken::String)) 153 return error("expected string in directive, instead got: ", Lexer->getTok()); 154 155 auto Kind = StringSwitch<Optional<SectionKind>>(Name) 156 .StartsWith(".data", SectionKind::getData()) 157 .StartsWith(".tdata", SectionKind::getThreadData()) 158 .StartsWith(".tbss", SectionKind::getThreadBSS()) 159 .StartsWith(".rodata", SectionKind::getReadOnly()) 160 .StartsWith(".text", SectionKind::getText()) 161 .StartsWith(".custom_section", SectionKind::getMetadata()) 162 .StartsWith(".bss", SectionKind::getBSS()) 163 // See use of .init_array in WasmObjectWriter and 164 // TargetLoweringObjectFileWasm 165 .StartsWith(".init_array", SectionKind::getData()) 166 .StartsWith(".debug_", SectionKind::getMetadata()) 167 .Default(SectionKind::getData()); 168 169 // Update section flags if present in this .section directive 170 bool Passive = false; 171 bool Group = false; 172 uint32_t Flags = 173 parseSectionFlags(getTok().getStringContents(), Passive, Group); 174 if (Flags == -1U) 175 return TokError("unknown flag"); 176 177 Lex(); 178 179 if (expect(AsmToken::Comma, ",") || expect(AsmToken::At, "@")) 180 return true; 181 182 StringRef GroupName; 183 if (Group && parseGroup(GroupName)) 184 return true; 185 186 if (expect(AsmToken::EndOfStatement, "eol")) 187 return true; 188 189 // TODO: Parse UniqueID 190 MCSectionWasm *WS = getContext().getWasmSection( 191 Name, *Kind, Flags, GroupName, MCContext::GenericSectionID); 192 193 if (WS->getSegmentFlags() != Flags) 194 Parser->Error(loc, "changed section flags for " + Name + 195 ", expected: 0x" + 196 utohexstr(WS->getSegmentFlags())); 197 198 if (Passive) { 199 if (!WS->isWasmData()) 200 return Parser->Error(loc, "Only data sections can be passive"); 201 WS->setPassive(); 202 } 203 204 getStreamer().switchSection(WS); 205 return false; 206 } 207 208 // TODO: This function is almost the same as ELFAsmParser::ParseDirectiveSize 209 // so maybe could be shared somehow. 210 bool parseDirectiveSize(StringRef, SMLoc) { 211 StringRef Name; 212 if (Parser->parseIdentifier(Name)) 213 return TokError("expected identifier in directive"); 214 auto Sym = getContext().getOrCreateSymbol(Name); 215 if (expect(AsmToken::Comma, ",")) 216 return true; 217 const MCExpr *Expr; 218 if (Parser->parseExpression(Expr)) 219 return true; 220 if (expect(AsmToken::EndOfStatement, "eol")) 221 return true; 222 // This is done automatically by the assembler for functions currently, 223 // so this is only currently needed for data sections: 224 getStreamer().emitELFSize(Sym, Expr); 225 return false; 226 } 227 228 bool parseDirectiveType(StringRef, SMLoc) { 229 // This could be the start of a function, check if followed by 230 // "label,@function" 231 if (!Lexer->is(AsmToken::Identifier)) 232 return error("Expected label after .type directive, got: ", 233 Lexer->getTok()); 234 auto WasmSym = cast<MCSymbolWasm>( 235 getStreamer().getContext().getOrCreateSymbol( 236 Lexer->getTok().getString())); 237 Lex(); 238 if (!(isNext(AsmToken::Comma) && isNext(AsmToken::At) && 239 Lexer->is(AsmToken::Identifier))) 240 return error("Expected label,@type declaration, got: ", Lexer->getTok()); 241 auto TypeName = Lexer->getTok().getString(); 242 if (TypeName == "function") { 243 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 244 auto *Current = 245 cast<MCSectionWasm>(getStreamer().getCurrentSection().first); 246 if (Current->getGroup()) 247 WasmSym->setComdat(true); 248 } else if (TypeName == "global") 249 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); 250 else if (TypeName == "object") 251 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA); 252 else 253 return error("Unknown WASM symbol type: ", Lexer->getTok()); 254 Lex(); 255 return expect(AsmToken::EndOfStatement, "EOL"); 256 } 257 258 // FIXME: Shared with ELF. 259 /// ParseDirectiveIdent 260 /// ::= .ident string 261 bool ParseDirectiveIdent(StringRef, SMLoc) { 262 if (getLexer().isNot(AsmToken::String)) 263 return TokError("unexpected token in '.ident' directive"); 264 StringRef Data = getTok().getIdentifier(); 265 Lex(); 266 if (getLexer().isNot(AsmToken::EndOfStatement)) 267 return TokError("unexpected token in '.ident' directive"); 268 Lex(); 269 getStreamer().emitIdent(Data); 270 return false; 271 } 272 273 // FIXME: Shared with ELF. 274 /// ParseDirectiveSymbolAttribute 275 /// ::= { ".local", ".weak", ... } [ identifier ( , identifier )* ] 276 bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) { 277 MCSymbolAttr Attr = StringSwitch<MCSymbolAttr>(Directive) 278 .Case(".weak", MCSA_Weak) 279 .Case(".local", MCSA_Local) 280 .Case(".hidden", MCSA_Hidden) 281 .Case(".internal", MCSA_Internal) 282 .Case(".protected", MCSA_Protected) 283 .Default(MCSA_Invalid); 284 assert(Attr != MCSA_Invalid && "unexpected symbol attribute directive!"); 285 if (getLexer().isNot(AsmToken::EndOfStatement)) { 286 while (true) { 287 StringRef Name; 288 if (getParser().parseIdentifier(Name)) 289 return TokError("expected identifier in directive"); 290 MCSymbol *Sym = getContext().getOrCreateSymbol(Name); 291 getStreamer().emitSymbolAttribute(Sym, Attr); 292 if (getLexer().is(AsmToken::EndOfStatement)) 293 break; 294 if (getLexer().isNot(AsmToken::Comma)) 295 return TokError("unexpected token in directive"); 296 Lex(); 297 } 298 } 299 Lex(); 300 return false; 301 } 302 }; 303 304 } // end anonymous namespace 305 306 namespace llvm { 307 308 MCAsmParserExtension *createWasmAsmParser() { 309 return new WasmAsmParser; 310 } 311 312 } // end namespace llvm 313