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