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/AsmLexer.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 AsmLexer *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, MCSection::NonUniqueID); 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 MCSymbol *Sym; 217 if (Parser->parseSymbol(Sym)) 218 return TokError("expected identifier in directive"); 219 if (expect(AsmToken::Comma, ",")) 220 return true; 221 const MCExpr *Expr; 222 if (Parser->parseExpression(Expr)) 223 return true; 224 if (expect(AsmToken::EndOfStatement, "eol")) 225 return true; 226 auto WasmSym = cast<MCSymbolWasm>(Sym); 227 if (WasmSym->isFunction()) { 228 // Ignore .size directives for function symbols. They get their size 229 // set automatically based on their content. 230 Warning(Loc, ".size directive ignored for function symbols"); 231 } else { 232 getStreamer().emitELFSize(Sym, Expr); 233 } 234 return false; 235 } 236 237 bool parseDirectiveType(StringRef, SMLoc) { 238 // This could be the start of a function, check if followed by 239 // "label,@function" 240 if (!Lexer->is(AsmToken::Identifier)) 241 return error("Expected label after .type directive, got: ", 242 Lexer->getTok()); 243 auto *WasmSym = cast<MCSymbolWasm>( 244 getStreamer().getContext().parseSymbol(Lexer->getTok().getString())); 245 Lex(); 246 if (!(isNext(AsmToken::Comma) && isNext(AsmToken::At) && 247 Lexer->is(AsmToken::Identifier))) 248 return error("Expected label,@type declaration, got: ", Lexer->getTok()); 249 auto TypeName = Lexer->getTok().getString(); 250 if (TypeName == "function") { 251 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); 252 auto *Current = 253 cast<MCSectionWasm>(getStreamer().getCurrentSectionOnly()); 254 if (Current->getGroup()) 255 WasmSym->setComdat(true); 256 } else if (TypeName == "global") 257 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); 258 else if (TypeName == "object") 259 WasmSym->setType(wasm::WASM_SYMBOL_TYPE_DATA); 260 else 261 return error("Unknown WASM symbol type: ", Lexer->getTok()); 262 Lex(); 263 return expect(AsmToken::EndOfStatement, "EOL"); 264 } 265 266 // FIXME: Shared with ELF. 267 /// ParseDirectiveIdent 268 /// ::= .ident string 269 bool ParseDirectiveIdent(StringRef, SMLoc) { 270 if (getLexer().isNot(AsmToken::String)) 271 return TokError("unexpected token in '.ident' directive"); 272 StringRef Data = getTok().getIdentifier(); 273 Lex(); 274 if (getLexer().isNot(AsmToken::EndOfStatement)) 275 return TokError("unexpected token in '.ident' directive"); 276 Lex(); 277 getStreamer().emitIdent(Data); 278 return false; 279 } 280 281 // FIXME: Shared with ELF. 282 /// ParseDirectiveSymbolAttribute 283 /// ::= { ".local", ".weak", ... } [ identifier ( , identifier )* ] 284 bool ParseDirectiveSymbolAttribute(StringRef Directive, SMLoc) { 285 MCSymbolAttr Attr = StringSwitch<MCSymbolAttr>(Directive) 286 .Case(".weak", MCSA_Weak) 287 .Case(".local", MCSA_Local) 288 .Case(".hidden", MCSA_Hidden) 289 .Case(".internal", MCSA_Internal) 290 .Case(".protected", MCSA_Protected) 291 .Default(MCSA_Invalid); 292 assert(Attr != MCSA_Invalid && "unexpected symbol attribute directive!"); 293 if (getLexer().isNot(AsmToken::EndOfStatement)) { 294 while (true) { 295 MCSymbol *Sym; 296 if (getParser().parseSymbol(Sym)) 297 return TokError("expected identifier in directive"); 298 getStreamer().emitSymbolAttribute(Sym, Attr); 299 if (getLexer().is(AsmToken::EndOfStatement)) 300 break; 301 if (getLexer().isNot(AsmToken::Comma)) 302 return TokError("unexpected token in directive"); 303 Lex(); 304 } 305 } 306 Lex(); 307 return false; 308 } 309 }; 310 311 } // end anonymous namespace 312 313 namespace llvm { 314 315 MCAsmParserExtension *createWasmAsmParser() { 316 return new WasmAsmParser; 317 } 318 319 } // end namespace llvm 320