1 | //==- WebAssemblyAsmParser.cpp - Assembler for WebAssembly -*- C++ -*-==// |
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 | /// \file |
10 | /// This file is part of the WebAssembly Assembler. |
11 | /// |
12 | /// It contains code to translate a parsed .s file into MCInsts. |
13 | /// |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #include "AsmParser/WebAssemblyAsmTypeCheck.h" |
17 | #include "MCTargetDesc/WebAssemblyMCAsmInfo.h" |
18 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
19 | #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h" |
20 | #include "MCTargetDesc/WebAssemblyTargetStreamer.h" |
21 | #include "TargetInfo/WebAssemblyTargetInfo.h" |
22 | #include "llvm/MC/MCContext.h" |
23 | #include "llvm/MC/MCExpr.h" |
24 | #include "llvm/MC/MCInst.h" |
25 | #include "llvm/MC/MCInstrInfo.h" |
26 | #include "llvm/MC/MCParser/AsmLexer.h" |
27 | #include "llvm/MC/MCParser/MCParsedAsmOperand.h" |
28 | #include "llvm/MC/MCParser/MCTargetAsmParser.h" |
29 | #include "llvm/MC/MCSectionWasm.h" |
30 | #include "llvm/MC/MCStreamer.h" |
31 | #include "llvm/MC/MCSubtargetInfo.h" |
32 | #include "llvm/MC/MCSymbol.h" |
33 | #include "llvm/MC/MCSymbolWasm.h" |
34 | #include "llvm/MC/TargetRegistry.h" |
35 | #include "llvm/Support/Compiler.h" |
36 | #include "llvm/Support/SourceMgr.h" |
37 | |
38 | using namespace llvm; |
39 | |
40 | #define DEBUG_TYPE "wasm-asm-parser" |
41 | |
42 | static const char *getSubtargetFeatureName(uint64_t Val); |
43 | |
44 | namespace { |
45 | |
46 | /// WebAssemblyOperand - Instances of this class represent the operands in a |
47 | /// parsed Wasm machine instruction. |
48 | struct WebAssemblyOperand : public MCParsedAsmOperand { |
49 | enum KindTy { Token, Integer, Float, Symbol, BrList, CatchList } Kind; |
50 | |
51 | SMLoc StartLoc, EndLoc; |
52 | |
53 | struct TokOp { |
54 | StringRef Tok; |
55 | }; |
56 | |
57 | struct IntOp { |
58 | int64_t Val; |
59 | }; |
60 | |
61 | struct FltOp { |
62 | double Val; |
63 | }; |
64 | |
65 | struct SymOp { |
66 | const MCExpr *Exp; |
67 | }; |
68 | |
69 | struct BrLOp { |
70 | std::vector<unsigned> List; |
71 | }; |
72 | |
73 | struct CaLOpElem { |
74 | uint8_t Opcode; |
75 | const MCExpr *Tag; |
76 | unsigned Dest; |
77 | }; |
78 | |
79 | struct CaLOp { |
80 | std::vector<CaLOpElem> List; |
81 | }; |
82 | |
83 | union { |
84 | struct TokOp Tok; |
85 | struct IntOp Int; |
86 | struct FltOp Flt; |
87 | struct SymOp Sym; |
88 | struct BrLOp BrL; |
89 | struct CaLOp CaL; |
90 | }; |
91 | |
92 | WebAssemblyOperand(SMLoc Start, SMLoc End, TokOp T) |
93 | : Kind(Token), StartLoc(Start), EndLoc(End), Tok(T) {} |
94 | WebAssemblyOperand(SMLoc Start, SMLoc End, IntOp I) |
95 | : Kind(Integer), StartLoc(Start), EndLoc(End), Int(I) {} |
96 | WebAssemblyOperand(SMLoc Start, SMLoc End, FltOp F) |
97 | : Kind(Float), StartLoc(Start), EndLoc(End), Flt(F) {} |
98 | WebAssemblyOperand(SMLoc Start, SMLoc End, SymOp S) |
99 | : Kind(Symbol), StartLoc(Start), EndLoc(End), Sym(S) {} |
100 | WebAssemblyOperand(SMLoc Start, SMLoc End, BrLOp B) |
101 | : Kind(BrList), StartLoc(Start), EndLoc(End), BrL(B) {} |
102 | WebAssemblyOperand(SMLoc Start, SMLoc End, CaLOp C) |
103 | : Kind(CatchList), StartLoc(Start), EndLoc(End), CaL(C) {} |
104 | |
105 | ~WebAssemblyOperand() { |
106 | if (isBrList()) |
107 | BrL.~BrLOp(); |
108 | if (isCatchList()) |
109 | CaL.~CaLOp(); |
110 | } |
111 | |
112 | bool isToken() const override { return Kind == Token; } |
113 | bool isImm() const override { return Kind == Integer || Kind == Symbol; } |
114 | bool isFPImm() const { return Kind == Float; } |
115 | bool isMem() const override { return false; } |
116 | bool isReg() const override { return false; } |
117 | bool isBrList() const { return Kind == BrList; } |
118 | bool isCatchList() const { return Kind == CatchList; } |
119 | |
120 | MCRegister getReg() const override { |
121 | llvm_unreachable("Assembly inspects a register operand" ); |
122 | return 0; |
123 | } |
124 | |
125 | StringRef getToken() const { |
126 | assert(isToken()); |
127 | return Tok.Tok; |
128 | } |
129 | |
130 | SMLoc getStartLoc() const override { return StartLoc; } |
131 | SMLoc getEndLoc() const override { return EndLoc; } |
132 | |
133 | void addRegOperands(MCInst &, unsigned) const { |
134 | // Required by the assembly matcher. |
135 | llvm_unreachable("Assembly matcher creates register operands" ); |
136 | } |
137 | |
138 | void addImmOperands(MCInst &Inst, unsigned N) const { |
139 | assert(N == 1 && "Invalid number of operands!" ); |
140 | if (Kind == Integer) |
141 | Inst.addOperand(Op: MCOperand::createImm(Val: Int.Val)); |
142 | else if (Kind == Symbol) |
143 | Inst.addOperand(Op: MCOperand::createExpr(Val: Sym.Exp)); |
144 | else |
145 | llvm_unreachable("Should be integer immediate or symbol!" ); |
146 | } |
147 | |
148 | void addFPImmf32Operands(MCInst &Inst, unsigned N) const { |
149 | assert(N == 1 && "Invalid number of operands!" ); |
150 | if (Kind == Float) |
151 | Inst.addOperand( |
152 | Op: MCOperand::createSFPImm(Val: bit_cast<uint32_t>(from: float(Flt.Val)))); |
153 | else |
154 | llvm_unreachable("Should be float immediate!" ); |
155 | } |
156 | |
157 | void addFPImmf64Operands(MCInst &Inst, unsigned N) const { |
158 | assert(N == 1 && "Invalid number of operands!" ); |
159 | if (Kind == Float) |
160 | Inst.addOperand(Op: MCOperand::createDFPImm(Val: bit_cast<uint64_t>(from: Flt.Val))); |
161 | else |
162 | llvm_unreachable("Should be float immediate!" ); |
163 | } |
164 | |
165 | void addBrListOperands(MCInst &Inst, unsigned N) const { |
166 | assert(N == 1 && isBrList() && "Invalid BrList!" ); |
167 | for (auto Br : BrL.List) |
168 | Inst.addOperand(Op: MCOperand::createImm(Val: Br)); |
169 | } |
170 | |
171 | void addCatchListOperands(MCInst &Inst, unsigned N) const { |
172 | assert(N == 1 && isCatchList() && "Invalid CatchList!" ); |
173 | Inst.addOperand(Op: MCOperand::createImm(Val: CaL.List.size())); |
174 | for (auto Ca : CaL.List) { |
175 | Inst.addOperand(Op: MCOperand::createImm(Val: Ca.Opcode)); |
176 | if (Ca.Opcode == wasm::WASM_OPCODE_CATCH || |
177 | Ca.Opcode == wasm::WASM_OPCODE_CATCH_REF) |
178 | Inst.addOperand(Op: MCOperand::createExpr(Val: Ca.Tag)); |
179 | Inst.addOperand(Op: MCOperand::createImm(Val: Ca.Dest)); |
180 | } |
181 | } |
182 | |
183 | void print(raw_ostream &OS, const MCAsmInfo &MAI) const override { |
184 | switch (Kind) { |
185 | case Token: |
186 | OS << "Tok:" << Tok.Tok; |
187 | break; |
188 | case Integer: |
189 | OS << "Int:" << Int.Val; |
190 | break; |
191 | case Float: |
192 | OS << "Flt:" << Flt.Val; |
193 | break; |
194 | case Symbol: |
195 | OS << "Sym:" << Sym.Exp; |
196 | break; |
197 | case BrList: |
198 | OS << "BrList:" << BrL.List.size(); |
199 | break; |
200 | case CatchList: |
201 | OS << "CaList:" << CaL.List.size(); |
202 | break; |
203 | } |
204 | } |
205 | }; |
206 | |
207 | // Perhaps this should go somewhere common. |
208 | static wasm::WasmLimits defaultLimits() { |
209 | return {.Flags: wasm::WASM_LIMITS_FLAG_NONE, .Minimum: 0, .Maximum: 0, .PageSize: 0}; |
210 | } |
211 | |
212 | static MCSymbolWasm *getOrCreateFunctionTableSymbol(MCContext &Ctx, |
213 | const StringRef &Name, |
214 | bool Is64) { |
215 | MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Val: Ctx.lookupSymbol(Name)); |
216 | if (Sym) { |
217 | if (!Sym->isFunctionTable()) |
218 | Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table" ); |
219 | } else { |
220 | Sym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name)); |
221 | Sym->setFunctionTable(Is64); |
222 | // The default function table is synthesized by the linker. |
223 | Sym->setUndefined(); |
224 | } |
225 | return Sym; |
226 | } |
227 | |
228 | class WebAssemblyAsmParser final : public MCTargetAsmParser { |
229 | MCAsmParser &Parser; |
230 | AsmLexer &Lexer; |
231 | |
232 | // Order of labels, directives and instructions in a .s file have no |
233 | // syntactical enforcement. This class is a callback from the actual parser, |
234 | // and yet we have to be feeding data to the streamer in a very particular |
235 | // order to ensure a correct binary encoding that matches the regular backend |
236 | // (the streamer does not enforce this). This "state machine" enum helps |
237 | // guarantee that correct order. |
238 | enum ParserState { |
239 | FileStart, |
240 | FunctionLabel, |
241 | FunctionStart, |
242 | FunctionLocals, |
243 | Instructions, |
244 | EndFunction, |
245 | DataSection, |
246 | } CurrentState = FileStart; |
247 | |
248 | // For ensuring blocks are properly nested. |
249 | enum NestingType { |
250 | Function, |
251 | Block, |
252 | Loop, |
253 | Try, |
254 | CatchAll, |
255 | TryTable, |
256 | If, |
257 | Else, |
258 | Undefined, |
259 | }; |
260 | struct Nested { |
261 | NestingType NT; |
262 | wasm::WasmSignature Sig; |
263 | }; |
264 | std::vector<Nested> NestingStack; |
265 | |
266 | MCSymbolWasm *DefaultFunctionTable = nullptr; |
267 | MCSymbol *LastFunctionLabel = nullptr; |
268 | |
269 | bool Is64; |
270 | |
271 | WebAssemblyAsmTypeCheck TC; |
272 | // Don't type check if -no-type-check was set. |
273 | bool SkipTypeCheck; |
274 | |
275 | public: |
276 | WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, |
277 | const MCInstrInfo &MII, const MCTargetOptions &Options) |
278 | : MCTargetAsmParser(Options, STI, MII), Parser(Parser), |
279 | Lexer(Parser.getLexer()), Is64(STI.getTargetTriple().isArch64Bit()), |
280 | TC(Parser, MII, Is64), SkipTypeCheck(Options.MCNoTypeCheck) { |
281 | FeatureBitset FBS = ComputeAvailableFeatures(FB: STI.getFeatureBits()); |
282 | |
283 | // bulk-memory implies bulk-memory-opt |
284 | if (FBS.test(I: WebAssembly::FeatureBulkMemory)) { |
285 | FBS.set(WebAssembly::FeatureBulkMemoryOpt); |
286 | } |
287 | // reference-types implies call-indirect-overlong |
288 | if (FBS.test(I: WebAssembly::FeatureReferenceTypes)) { |
289 | FBS.set(WebAssembly::FeatureCallIndirectOverlong); |
290 | } |
291 | |
292 | setAvailableFeatures(FBS); |
293 | // Don't type check if this is inline asm, since that is a naked sequence of |
294 | // instructions without a function/locals decl. |
295 | auto &SM = Parser.getSourceManager(); |
296 | auto BufferName = |
297 | SM.getBufferInfo(i: SM.getMainFileID()).Buffer->getBufferIdentifier(); |
298 | if (BufferName == "<inline asm>" ) |
299 | SkipTypeCheck = true; |
300 | } |
301 | |
302 | void Initialize(MCAsmParser &Parser) override { |
303 | MCAsmParserExtension::Initialize(Parser); |
304 | |
305 | DefaultFunctionTable = getOrCreateFunctionTableSymbol( |
306 | Ctx&: getContext(), Name: "__indirect_function_table" , Is64); |
307 | if (!STI->checkFeatures(FS: "+call-indirect-overlong" ) && |
308 | !STI->checkFeatures(FS: "+reference-types" )) |
309 | DefaultFunctionTable->setOmitFromLinkingSection(); |
310 | } |
311 | |
312 | #define |
313 | #include "WebAssemblyGenAsmMatcher.inc" |
314 | |
315 | // TODO: This is required to be implemented, but appears unused. |
316 | bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override { |
317 | llvm_unreachable("parseRegister is not implemented." ); |
318 | } |
319 | ParseStatus tryParseRegister(MCRegister &Reg, SMLoc &StartLoc, |
320 | SMLoc &EndLoc) override { |
321 | llvm_unreachable("tryParseRegister is not implemented." ); |
322 | } |
323 | |
324 | bool error(const Twine &Msg, const AsmToken &Tok) { |
325 | return Parser.Error(L: Tok.getLoc(), Msg: Msg + Tok.getString()); |
326 | } |
327 | |
328 | bool error(const Twine &Msg, SMLoc Loc = SMLoc()) { |
329 | return Parser.Error(L: Loc.isValid() ? Loc : Lexer.getTok().getLoc(), Msg); |
330 | } |
331 | |
332 | std::pair<StringRef, StringRef> nestingString(NestingType NT) { |
333 | switch (NT) { |
334 | case Function: |
335 | return {"function" , "end_function" }; |
336 | case Block: |
337 | return {"block" , "end_block" }; |
338 | case Loop: |
339 | return {"loop" , "end_loop" }; |
340 | case Try: |
341 | return {"try" , "end_try/delegate" }; |
342 | case CatchAll: |
343 | return {"catch_all" , "end_try" }; |
344 | case TryTable: |
345 | return {"try_table" , "end_try_table" }; |
346 | case If: |
347 | return {"if" , "end_if" }; |
348 | case Else: |
349 | return {"else" , "end_if" }; |
350 | default: |
351 | llvm_unreachable("unknown NestingType" ); |
352 | } |
353 | } |
354 | |
355 | void push(NestingType NT, wasm::WasmSignature Sig = wasm::WasmSignature()) { |
356 | NestingStack.push_back(x: {.NT: NT, .Sig: Sig}); |
357 | } |
358 | |
359 | bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) { |
360 | if (NestingStack.empty()) |
361 | return error(Msg: Twine("End of block construct with no start: " ) + Ins); |
362 | auto Top = NestingStack.back(); |
363 | if (Top.NT != NT1 && Top.NT != NT2) |
364 | return error(Msg: Twine("Block construct type mismatch, expected: " ) + |
365 | nestingString(NT: Top.NT).second + ", instead got: " + Ins); |
366 | TC.setLastSig(Top.Sig); |
367 | NestingStack.pop_back(); |
368 | return false; |
369 | } |
370 | |
371 | // Pop a NestingType and push a new NestingType with the same signature. Used |
372 | // for if-else and try-catch(_all). |
373 | bool popAndPushWithSameSignature(StringRef Ins, NestingType PopNT, |
374 | NestingType PushNT) { |
375 | if (NestingStack.empty()) |
376 | return error(Msg: Twine("End of block construct with no start: " ) + Ins); |
377 | auto Sig = NestingStack.back().Sig; |
378 | if (pop(Ins, NT1: PopNT)) |
379 | return true; |
380 | push(NT: PushNT, Sig); |
381 | return false; |
382 | } |
383 | |
384 | bool ensureEmptyNestingStack(SMLoc Loc = SMLoc()) { |
385 | auto Err = !NestingStack.empty(); |
386 | while (!NestingStack.empty()) { |
387 | error(Msg: Twine("Unmatched block construct(s) at function end: " ) + |
388 | nestingString(NT: NestingStack.back().NT).first, |
389 | Loc); |
390 | NestingStack.pop_back(); |
391 | } |
392 | return Err; |
393 | } |
394 | |
395 | bool isNext(AsmToken::TokenKind Kind) { |
396 | auto Ok = Lexer.is(K: Kind); |
397 | if (Ok) |
398 | Parser.Lex(); |
399 | return Ok; |
400 | } |
401 | |
402 | bool expect(AsmToken::TokenKind Kind, const char *KindName) { |
403 | if (!isNext(Kind)) |
404 | return error(Msg: std::string("Expected " ) + KindName + ", instead got: " , |
405 | Tok: Lexer.getTok()); |
406 | return false; |
407 | } |
408 | |
409 | StringRef expectIdent() { |
410 | if (!Lexer.is(K: AsmToken::Identifier)) { |
411 | error(Msg: "Expected identifier, got: " , Tok: Lexer.getTok()); |
412 | return StringRef(); |
413 | } |
414 | auto Name = Lexer.getTok().getString(); |
415 | Parser.Lex(); |
416 | return Name; |
417 | } |
418 | |
419 | bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) { |
420 | while (Lexer.is(K: AsmToken::Identifier)) { |
421 | auto Type = WebAssembly::parseType(Type: Lexer.getTok().getString()); |
422 | if (!Type) |
423 | return error(Msg: "unknown type: " , Tok: Lexer.getTok()); |
424 | Types.push_back(Elt: *Type); |
425 | Parser.Lex(); |
426 | if (!isNext(Kind: AsmToken::Comma)) |
427 | break; |
428 | } |
429 | return false; |
430 | } |
431 | |
432 | void parseSingleInteger(bool IsNegative, OperandVector &Operands) { |
433 | auto &Int = Lexer.getTok(); |
434 | int64_t Val = Int.getIntVal(); |
435 | if (IsNegative) |
436 | Val = -Val; |
437 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
438 | args: Int.getLoc(), args: Int.getEndLoc(), args: WebAssemblyOperand::IntOp{.Val: Val})); |
439 | Parser.Lex(); |
440 | } |
441 | |
442 | bool parseSingleFloat(bool IsNegative, OperandVector &Operands) { |
443 | auto &Flt = Lexer.getTok(); |
444 | double Val; |
445 | if (Flt.getString().getAsDouble(Result&: Val, AllowInexact: false)) |
446 | return error(Msg: "Cannot parse real: " , Tok: Flt); |
447 | if (IsNegative) |
448 | Val = -Val; |
449 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
450 | args: Flt.getLoc(), args: Flt.getEndLoc(), args: WebAssemblyOperand::FltOp{.Val: Val})); |
451 | Parser.Lex(); |
452 | return false; |
453 | } |
454 | |
455 | bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) { |
456 | if (Lexer.isNot(K: AsmToken::Identifier)) |
457 | return true; |
458 | auto &Flt = Lexer.getTok(); |
459 | auto S = Flt.getString(); |
460 | double Val; |
461 | if (S.compare_insensitive(RHS: "infinity" ) == 0) { |
462 | Val = std::numeric_limits<double>::infinity(); |
463 | } else if (S.compare_insensitive(RHS: "nan" ) == 0) { |
464 | Val = std::numeric_limits<double>::quiet_NaN(); |
465 | } else { |
466 | return true; |
467 | } |
468 | if (IsNegative) |
469 | Val = -Val; |
470 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
471 | args: Flt.getLoc(), args: Flt.getEndLoc(), args: WebAssemblyOperand::FltOp{.Val: Val})); |
472 | Parser.Lex(); |
473 | return false; |
474 | } |
475 | |
476 | bool checkForP2AlignIfLoadStore(OperandVector &Operands, StringRef InstName) { |
477 | // FIXME: there is probably a cleaner way to do this. |
478 | auto IsLoadStore = InstName.contains(Other: ".load" ) || |
479 | InstName.contains(Other: ".store" ) || |
480 | InstName.contains(Other: "prefetch" ); |
481 | auto IsAtomic = InstName.contains(Other: "atomic." ); |
482 | if (IsLoadStore || IsAtomic) { |
483 | // Parse load/store operands of the form: offset:p2align=align |
484 | if (IsLoadStore && isNext(Kind: AsmToken::Colon)) { |
485 | auto Id = expectIdent(); |
486 | if (Id != "p2align" ) |
487 | return error(Msg: "Expected p2align, instead got: " + Id); |
488 | if (expect(Kind: AsmToken::Equal, KindName: "=" )) |
489 | return true; |
490 | if (!Lexer.is(K: AsmToken::Integer)) |
491 | return error(Msg: "Expected integer constant" ); |
492 | parseSingleInteger(IsNegative: false, Operands); |
493 | } else { |
494 | // v128.{load,store}{8,16,32,64}_lane has both a memarg and a lane |
495 | // index. We need to avoid parsing an extra alignment operand for the |
496 | // lane index. |
497 | auto IsLoadStoreLane = InstName.contains(Other: "_lane" ); |
498 | if (IsLoadStoreLane && Operands.size() == 4) |
499 | return false; |
500 | // Alignment not specified (or atomics, must use default alignment). |
501 | // We can't just call WebAssembly::GetDefaultP2Align since we don't have |
502 | // an opcode until after the assembly matcher, so set a default to fix |
503 | // up later. |
504 | auto Tok = Lexer.getTok(); |
505 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
506 | args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::IntOp{.Val: -1})); |
507 | } |
508 | } |
509 | return false; |
510 | } |
511 | |
512 | void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc, |
513 | WebAssembly::BlockType BT) { |
514 | if (BT == WebAssembly::BlockType::Void) { |
515 | TC.setLastSig(wasm::WasmSignature{}); |
516 | } else { |
517 | wasm::WasmSignature Sig({static_cast<wasm::ValType>(BT)}, {}); |
518 | TC.setLastSig(Sig); |
519 | NestingStack.back().Sig = Sig; |
520 | } |
521 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
522 | args&: NameLoc, args&: NameLoc, args: WebAssemblyOperand::IntOp{.Val: static_cast<int64_t>(BT)})); |
523 | } |
524 | |
525 | bool parseLimits(wasm::WasmLimits *Limits) { |
526 | auto Tok = Lexer.getTok(); |
527 | if (!Tok.is(K: AsmToken::Integer)) |
528 | return error(Msg: "Expected integer constant, instead got: " , Tok); |
529 | int64_t Val = Tok.getIntVal(); |
530 | assert(Val >= 0); |
531 | Limits->Minimum = Val; |
532 | Parser.Lex(); |
533 | |
534 | if (isNext(Kind: AsmToken::Comma)) { |
535 | Limits->Flags |= wasm::WASM_LIMITS_FLAG_HAS_MAX; |
536 | auto Tok = Lexer.getTok(); |
537 | if (!Tok.is(K: AsmToken::Integer)) |
538 | return error(Msg: "Expected integer constant, instead got: " , Tok); |
539 | int64_t Val = Tok.getIntVal(); |
540 | assert(Val >= 0); |
541 | Limits->Maximum = Val; |
542 | Parser.Lex(); |
543 | } |
544 | return false; |
545 | } |
546 | |
547 | bool parseFunctionTableOperand(std::unique_ptr<WebAssemblyOperand> *Op) { |
548 | if (STI->checkFeatures(FS: "+call-indirect-overlong" ) || |
549 | STI->checkFeatures(FS: "+reference-types" )) { |
550 | // If the call-indirect-overlong feature is enabled, or implied by the |
551 | // reference-types feature, there is an explicit table operand. To allow |
552 | // the same assembly to be compiled with or without |
553 | // call-indirect-overlong, we allow the operand to be omitted, in which |
554 | // case we default to __indirect_function_table. |
555 | auto &Tok = Lexer.getTok(); |
556 | if (Tok.is(K: AsmToken::Identifier)) { |
557 | auto *Sym = |
558 | getOrCreateFunctionTableSymbol(Ctx&: getContext(), Name: Tok.getString(), Is64); |
559 | const auto *Val = MCSymbolRefExpr::create(Symbol: Sym, Ctx&: getContext()); |
560 | *Op = std::make_unique<WebAssemblyOperand>( |
561 | args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::SymOp{.Exp: Val}); |
562 | Parser.Lex(); |
563 | return expect(Kind: AsmToken::Comma, KindName: "," ); |
564 | } |
565 | const auto *Val = |
566 | MCSymbolRefExpr::create(Symbol: DefaultFunctionTable, Ctx&: getContext()); |
567 | *Op = std::make_unique<WebAssemblyOperand>( |
568 | args: SMLoc(), args: SMLoc(), args: WebAssemblyOperand::SymOp{.Exp: Val}); |
569 | return false; |
570 | } |
571 | // For the MVP there is at most one table whose number is 0, but we can't |
572 | // write a table symbol or issue relocations. Instead we just ensure the |
573 | // table is live and write a zero. |
574 | getStreamer().emitSymbolAttribute(Symbol: DefaultFunctionTable, Attribute: MCSA_NoDeadStrip); |
575 | *Op = std::make_unique<WebAssemblyOperand>(args: SMLoc(), args: SMLoc(), |
576 | args: WebAssemblyOperand::IntOp{.Val: 0}); |
577 | return false; |
578 | } |
579 | |
580 | bool parseInstruction(ParseInstructionInfo & /*Info*/, StringRef Name, |
581 | SMLoc NameLoc, OperandVector &Operands) override { |
582 | // Note: Name does NOT point into the sourcecode, but to a local, so |
583 | // use NameLoc instead. |
584 | Name = StringRef(NameLoc.getPointer(), Name.size()); |
585 | |
586 | // WebAssembly has instructions with / in them, which AsmLexer parses |
587 | // as separate tokens, so if we find such tokens immediately adjacent (no |
588 | // whitespace), expand the name to include them: |
589 | for (;;) { |
590 | auto &Sep = Lexer.getTok(); |
591 | if (Sep.getLoc().getPointer() != Name.end() || |
592 | Sep.getKind() != AsmToken::Slash) |
593 | break; |
594 | // Extend name with / |
595 | Name = StringRef(Name.begin(), Name.size() + Sep.getString().size()); |
596 | Parser.Lex(); |
597 | // We must now find another identifier, or error. |
598 | auto &Id = Lexer.getTok(); |
599 | if (Id.getKind() != AsmToken::Identifier || |
600 | Id.getLoc().getPointer() != Name.end()) |
601 | return error(Msg: "Incomplete instruction name: " , Tok: Id); |
602 | Name = StringRef(Name.begin(), Name.size() + Id.getString().size()); |
603 | Parser.Lex(); |
604 | } |
605 | |
606 | // Now construct the name as first operand. |
607 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
608 | args&: NameLoc, args: SMLoc::getFromPointer(Ptr: Name.end()), |
609 | args: WebAssemblyOperand::TokOp{.Tok: Name})); |
610 | |
611 | // If this instruction is part of a control flow structure, ensure |
612 | // proper nesting. |
613 | bool ExpectBlockType = false; |
614 | bool ExpectFuncType = false; |
615 | bool ExpectCatchList = false; |
616 | std::unique_ptr<WebAssemblyOperand> FunctionTable; |
617 | if (Name == "block" ) { |
618 | push(NT: Block); |
619 | ExpectBlockType = true; |
620 | } else if (Name == "loop" ) { |
621 | push(NT: Loop); |
622 | ExpectBlockType = true; |
623 | } else if (Name == "try" ) { |
624 | push(NT: Try); |
625 | ExpectBlockType = true; |
626 | } else if (Name == "if" ) { |
627 | push(NT: If); |
628 | ExpectBlockType = true; |
629 | } else if (Name == "else" ) { |
630 | if (popAndPushWithSameSignature(Ins: Name, PopNT: If, PushNT: Else)) |
631 | return true; |
632 | } else if (Name == "catch" ) { |
633 | if (popAndPushWithSameSignature(Ins: Name, PopNT: Try, PushNT: Try)) |
634 | return true; |
635 | } else if (Name == "catch_all" ) { |
636 | if (popAndPushWithSameSignature(Ins: Name, PopNT: Try, PushNT: CatchAll)) |
637 | return true; |
638 | } else if (Name == "try_table" ) { |
639 | push(NT: TryTable); |
640 | ExpectBlockType = true; |
641 | ExpectCatchList = true; |
642 | } else if (Name == "end_if" ) { |
643 | if (pop(Ins: Name, NT1: If, NT2: Else)) |
644 | return true; |
645 | } else if (Name == "end_try" ) { |
646 | if (pop(Ins: Name, NT1: Try, NT2: CatchAll)) |
647 | return true; |
648 | } else if (Name == "end_try_table" ) { |
649 | if (pop(Ins: Name, NT1: TryTable)) |
650 | return true; |
651 | } else if (Name == "delegate" ) { |
652 | if (pop(Ins: Name, NT1: Try)) |
653 | return true; |
654 | } else if (Name == "end_loop" ) { |
655 | if (pop(Ins: Name, NT1: Loop)) |
656 | return true; |
657 | } else if (Name == "end_block" ) { |
658 | if (pop(Ins: Name, NT1: Block)) |
659 | return true; |
660 | } else if (Name == "end_function" ) { |
661 | ensureLocals(Out&: getStreamer()); |
662 | CurrentState = EndFunction; |
663 | if (pop(Ins: Name, NT1: Function) || ensureEmptyNestingStack()) |
664 | return true; |
665 | } else if (Name == "call_indirect" || Name == "return_call_indirect" ) { |
666 | // These instructions have differing operand orders in the text format vs |
667 | // the binary formats. The MC instructions follow the binary format, so |
668 | // here we stash away the operand and append it later. |
669 | if (parseFunctionTableOperand(Op: &FunctionTable)) |
670 | return true; |
671 | ExpectFuncType = true; |
672 | } |
673 | |
674 | // Returns true if the next tokens are a catch clause |
675 | auto PeekCatchList = [&]() { |
676 | if (Lexer.isNot(K: AsmToken::LParen)) |
677 | return false; |
678 | AsmToken NextTok = Lexer.peekTok(); |
679 | return NextTok.getKind() == AsmToken::Identifier && |
680 | NextTok.getIdentifier().starts_with(Prefix: "catch" ); |
681 | }; |
682 | |
683 | // Parse a multivalue block type |
684 | if (ExpectFuncType || |
685 | (Lexer.is(K: AsmToken::LParen) && ExpectBlockType && !PeekCatchList())) { |
686 | // This has a special TYPEINDEX operand which in text we |
687 | // represent as a signature, such that we can re-build this signature, |
688 | // attach it to an anonymous symbol, which is what WasmObjectWriter |
689 | // expects to be able to recreate the actual unique-ified type indices. |
690 | auto &Ctx = getContext(); |
691 | auto Loc = Parser.getTok(); |
692 | auto *Signature = Ctx.createWasmSignature(); |
693 | if (parseSignature(Signature)) |
694 | return true; |
695 | // Got signature as block type, don't need more |
696 | TC.setLastSig(*Signature); |
697 | if (ExpectBlockType) |
698 | NestingStack.back().Sig = *Signature; |
699 | ExpectBlockType = false; |
700 | // The "true" here will cause this to be a nameless symbol. |
701 | MCSymbol *Sym = Ctx.createTempSymbol(Name: "typeindex" , AlwaysAddSuffix: true); |
702 | auto *WasmSym = cast<MCSymbolWasm>(Val: Sym); |
703 | WasmSym->setSignature(Signature); |
704 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); |
705 | const MCExpr *Expr = |
706 | MCSymbolRefExpr::create(Symbol: WasmSym, specifier: WebAssembly::S_TYPEINDEX, Ctx); |
707 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
708 | args: Loc.getLoc(), args: Loc.getEndLoc(), args: WebAssemblyOperand::SymOp{.Exp: Expr})); |
709 | } |
710 | |
711 | // If we are expecting a catch clause list, try to parse it here. |
712 | // |
713 | // If there is a multivalue block return type before this catch list, it |
714 | // should have been parsed above. If there is no return type before |
715 | // encountering this catch list, this means the type is void. |
716 | // The case when there is a single block return value and then a catch list |
717 | // will be handled below in the 'while' loop. |
718 | if (ExpectCatchList && PeekCatchList()) { |
719 | if (ExpectBlockType) { |
720 | ExpectBlockType = false; |
721 | addBlockTypeOperand(Operands, NameLoc, BT: WebAssembly::BlockType::Void); |
722 | } |
723 | if (parseCatchList(Operands)) |
724 | return true; |
725 | ExpectCatchList = false; |
726 | } |
727 | |
728 | while (Lexer.isNot(K: AsmToken::EndOfStatement)) { |
729 | auto &Tok = Lexer.getTok(); |
730 | switch (Tok.getKind()) { |
731 | case AsmToken::Identifier: { |
732 | if (!parseSpecialFloatMaybe(IsNegative: false, Operands)) |
733 | break; |
734 | auto &Id = Lexer.getTok(); |
735 | if (ExpectBlockType) { |
736 | // Assume this identifier is a block_type. |
737 | auto BT = WebAssembly::parseBlockType(Type: Id.getString()); |
738 | if (BT == WebAssembly::BlockType::Invalid) |
739 | return error(Msg: "Unknown block type: " , Tok: Id); |
740 | addBlockTypeOperand(Operands, NameLoc, BT); |
741 | ExpectBlockType = false; |
742 | Parser.Lex(); |
743 | // Now that we've parsed a single block return type, if we are |
744 | // expecting a catch clause list, try to parse it. |
745 | if (ExpectCatchList && PeekCatchList()) { |
746 | if (parseCatchList(Operands)) |
747 | return true; |
748 | ExpectCatchList = false; |
749 | } |
750 | } else { |
751 | // Assume this identifier is a label. |
752 | const MCExpr *Val; |
753 | SMLoc Start = Id.getLoc(); |
754 | SMLoc End; |
755 | if (Parser.parseExpression(Res&: Val, EndLoc&: End)) |
756 | return error(Msg: "Cannot parse symbol: " , Tok: Lexer.getTok()); |
757 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
758 | args&: Start, args&: End, args: WebAssemblyOperand::SymOp{.Exp: Val})); |
759 | if (checkForP2AlignIfLoadStore(Operands, InstName: Name)) |
760 | return true; |
761 | } |
762 | break; |
763 | } |
764 | case AsmToken::Minus: |
765 | Parser.Lex(); |
766 | if (Lexer.is(K: AsmToken::Integer)) { |
767 | parseSingleInteger(IsNegative: true, Operands); |
768 | if (checkForP2AlignIfLoadStore(Operands, InstName: Name)) |
769 | return true; |
770 | } else if (Lexer.is(K: AsmToken::Real)) { |
771 | if (parseSingleFloat(IsNegative: true, Operands)) |
772 | return true; |
773 | } else if (!parseSpecialFloatMaybe(IsNegative: true, Operands)) { |
774 | } else { |
775 | return error(Msg: "Expected numeric constant instead got: " , |
776 | Tok: Lexer.getTok()); |
777 | } |
778 | break; |
779 | case AsmToken::Integer: |
780 | parseSingleInteger(IsNegative: false, Operands); |
781 | if (checkForP2AlignIfLoadStore(Operands, InstName: Name)) |
782 | return true; |
783 | break; |
784 | case AsmToken::Real: { |
785 | if (parseSingleFloat(IsNegative: false, Operands)) |
786 | return true; |
787 | break; |
788 | } |
789 | case AsmToken::LCurly: { |
790 | Parser.Lex(); |
791 | auto Op = std::make_unique<WebAssemblyOperand>( |
792 | args: Tok.getLoc(), args: Tok.getEndLoc(), args: WebAssemblyOperand::BrLOp{}); |
793 | if (!Lexer.is(K: AsmToken::RCurly)) |
794 | for (;;) { |
795 | Op->BrL.List.push_back(x: Lexer.getTok().getIntVal()); |
796 | expect(Kind: AsmToken::Integer, KindName: "integer" ); |
797 | if (!isNext(Kind: AsmToken::Comma)) |
798 | break; |
799 | } |
800 | expect(Kind: AsmToken::RCurly, KindName: "}" ); |
801 | Operands.push_back(Elt: std::move(Op)); |
802 | break; |
803 | } |
804 | default: |
805 | return error(Msg: "Unexpected token in operand: " , Tok); |
806 | } |
807 | if (Lexer.isNot(K: AsmToken::EndOfStatement)) { |
808 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
809 | return true; |
810 | } |
811 | } |
812 | |
813 | // If we are still expecting to parse a block type or a catch list at this |
814 | // point, we set them to the default/empty state. |
815 | |
816 | // Support blocks with no operands as default to void. |
817 | if (ExpectBlockType) |
818 | addBlockTypeOperand(Operands, NameLoc, BT: WebAssembly::BlockType::Void); |
819 | // If no catch list has been parsed, add an empty catch list operand. |
820 | if (ExpectCatchList) |
821 | Operands.push_back(Elt: std::make_unique<WebAssemblyOperand>( |
822 | args&: NameLoc, args&: NameLoc, args: WebAssemblyOperand::CaLOp{})); |
823 | |
824 | if (FunctionTable) |
825 | Operands.push_back(Elt: std::move(FunctionTable)); |
826 | Parser.Lex(); |
827 | return false; |
828 | } |
829 | |
830 | bool parseSignature(wasm::WasmSignature *Signature) { |
831 | if (expect(Kind: AsmToken::LParen, KindName: "(" )) |
832 | return true; |
833 | if (parseRegTypeList(Types&: Signature->Params)) |
834 | return true; |
835 | if (expect(Kind: AsmToken::RParen, KindName: ")" )) |
836 | return true; |
837 | if (expect(Kind: AsmToken::MinusGreater, KindName: "->" )) |
838 | return true; |
839 | if (expect(Kind: AsmToken::LParen, KindName: "(" )) |
840 | return true; |
841 | if (parseRegTypeList(Types&: Signature->Returns)) |
842 | return true; |
843 | if (expect(Kind: AsmToken::RParen, KindName: ")" )) |
844 | return true; |
845 | return false; |
846 | } |
847 | |
848 | bool parseCatchList(OperandVector &Operands) { |
849 | auto Op = std::make_unique<WebAssemblyOperand>( |
850 | args: Lexer.getTok().getLoc(), args: SMLoc(), args: WebAssemblyOperand::CaLOp{}); |
851 | SMLoc EndLoc; |
852 | |
853 | while (Lexer.is(K: AsmToken::LParen)) { |
854 | if (expect(Kind: AsmToken::LParen, KindName: "(" )) |
855 | return true; |
856 | |
857 | auto CatchStr = expectIdent(); |
858 | if (CatchStr.empty()) |
859 | return true; |
860 | uint8_t CatchOpcode = |
861 | StringSwitch<uint8_t>(CatchStr) |
862 | .Case(S: "catch" , Value: wasm::WASM_OPCODE_CATCH) |
863 | .Case(S: "catch_ref" , Value: wasm::WASM_OPCODE_CATCH_REF) |
864 | .Case(S: "catch_all" , Value: wasm::WASM_OPCODE_CATCH_ALL) |
865 | .Case(S: "catch_all_ref" , Value: wasm::WASM_OPCODE_CATCH_ALL_REF) |
866 | .Default(Value: 0xff); |
867 | if (CatchOpcode == 0xff) |
868 | return error( |
869 | Msg: "Expected catch/catch_ref/catch_all/catch_all_ref, instead got: " + |
870 | CatchStr); |
871 | |
872 | const MCExpr *Tag = nullptr; |
873 | if (CatchOpcode == wasm::WASM_OPCODE_CATCH || |
874 | CatchOpcode == wasm::WASM_OPCODE_CATCH_REF) { |
875 | if (Parser.parseExpression(Res&: Tag)) |
876 | return error(Msg: "Cannot parse symbol: " , Tok: Lexer.getTok()); |
877 | } |
878 | |
879 | auto &DestTok = Lexer.getTok(); |
880 | if (DestTok.isNot(K: AsmToken::Integer)) |
881 | return error(Msg: "Expected integer constant, instead got: " , Tok: DestTok); |
882 | unsigned Dest = DestTok.getIntVal(); |
883 | Parser.Lex(); |
884 | |
885 | EndLoc = Lexer.getTok().getEndLoc(); |
886 | if (expect(Kind: AsmToken::RParen, KindName: ")" )) |
887 | return true; |
888 | |
889 | Op->CaL.List.push_back(x: {.Opcode: CatchOpcode, .Tag: Tag, .Dest: Dest}); |
890 | } |
891 | |
892 | Op->EndLoc = EndLoc; |
893 | Operands.push_back(Elt: std::move(Op)); |
894 | return false; |
895 | } |
896 | |
897 | bool checkDataSection() { |
898 | if (CurrentState != DataSection) { |
899 | auto *WS = cast<MCSectionWasm>(Val: getStreamer().getCurrentSectionOnly()); |
900 | if (WS && WS->isText()) |
901 | return error(Msg: "data directive must occur in a data segment: " , |
902 | Tok: Lexer.getTok()); |
903 | } |
904 | CurrentState = DataSection; |
905 | return false; |
906 | } |
907 | |
908 | // This function processes wasm-specific directives streamed to |
909 | // WebAssemblyTargetStreamer, all others go to the generic parser |
910 | // (see WasmAsmParser). |
911 | ParseStatus parseDirective(AsmToken DirectiveID) override { |
912 | assert(DirectiveID.getKind() == AsmToken::Identifier); |
913 | auto &Out = getStreamer(); |
914 | auto &TOut = |
915 | reinterpret_cast<WebAssemblyTargetStreamer &>(*Out.getTargetStreamer()); |
916 | auto &Ctx = Out.getContext(); |
917 | |
918 | if (DirectiveID.getString() == ".globaltype" ) { |
919 | auto SymName = expectIdent(); |
920 | if (SymName.empty()) |
921 | return ParseStatus::Failure; |
922 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
923 | return ParseStatus::Failure; |
924 | auto TypeTok = Lexer.getTok(); |
925 | auto TypeName = expectIdent(); |
926 | if (TypeName.empty()) |
927 | return ParseStatus::Failure; |
928 | auto Type = WebAssembly::parseType(Type: TypeName); |
929 | if (!Type) |
930 | return error(Msg: "Unknown type in .globaltype directive: " , Tok: TypeTok); |
931 | // Optional mutable modifier. Default to mutable for historical reasons. |
932 | // Ideally we would have gone with immutable as the default and used `mut` |
933 | // as the modifier to match the `.wat` format. |
934 | bool Mutable = true; |
935 | if (isNext(Kind: AsmToken::Comma)) { |
936 | TypeTok = Lexer.getTok(); |
937 | auto Id = expectIdent(); |
938 | if (Id.empty()) |
939 | return ParseStatus::Failure; |
940 | if (Id == "immutable" ) |
941 | Mutable = false; |
942 | else |
943 | // Should we also allow `mutable` and `mut` here for clarity? |
944 | return error(Msg: "Unknown type in .globaltype modifier: " , Tok: TypeTok); |
945 | } |
946 | // Now set this symbol with the correct type. |
947 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
948 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL); |
949 | WasmSym->setGlobalType(wasm::WasmGlobalType{.Type: uint8_t(*Type), .Mutable: Mutable}); |
950 | // And emit the directive again. |
951 | TOut.emitGlobalType(Sym: WasmSym); |
952 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
953 | } |
954 | |
955 | if (DirectiveID.getString() == ".tabletype" ) { |
956 | // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]] |
957 | auto SymName = expectIdent(); |
958 | if (SymName.empty()) |
959 | return ParseStatus::Failure; |
960 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
961 | return ParseStatus::Failure; |
962 | |
963 | auto ElemTypeTok = Lexer.getTok(); |
964 | auto ElemTypeName = expectIdent(); |
965 | if (ElemTypeName.empty()) |
966 | return ParseStatus::Failure; |
967 | std::optional<wasm::ValType> ElemType = |
968 | WebAssembly::parseType(Type: ElemTypeName); |
969 | if (!ElemType) |
970 | return error(Msg: "Unknown type in .tabletype directive: " , Tok: ElemTypeTok); |
971 | |
972 | wasm::WasmLimits Limits = defaultLimits(); |
973 | if (isNext(Kind: AsmToken::Comma) && parseLimits(Limits: &Limits)) |
974 | return ParseStatus::Failure; |
975 | |
976 | // Now that we have the name and table type, we can actually create the |
977 | // symbol |
978 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
979 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); |
980 | if (Is64) { |
981 | Limits.Flags |= wasm::WASM_LIMITS_FLAG_IS_64; |
982 | } |
983 | wasm::WasmTableType Type = {.ElemType: *ElemType, .Limits: Limits}; |
984 | WasmSym->setTableType(Type); |
985 | TOut.emitTableType(Sym: WasmSym); |
986 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
987 | } |
988 | |
989 | if (DirectiveID.getString() == ".functype" ) { |
990 | // This code has to send things to the streamer similar to |
991 | // WebAssemblyAsmPrinter::EmitFunctionBodyStart. |
992 | // TODO: would be good to factor this into a common function, but the |
993 | // assembler and backend really don't share any common code, and this code |
994 | // parses the locals separately. |
995 | auto SymName = expectIdent(); |
996 | if (SymName.empty()) |
997 | return ParseStatus::Failure; |
998 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
999 | if (WasmSym->isDefined()) { |
1000 | // We push 'Function' either when a label is parsed or a .functype |
1001 | // directive is parsed. The reason it is not easy to do this uniformly |
1002 | // in a single place is, |
1003 | // 1. We can't do this at label parsing time only because there are |
1004 | // cases we don't have .functype directive before a function label, |
1005 | // in which case we don't know if the label is a function at the time |
1006 | // of parsing. |
1007 | // 2. We can't do this at .functype parsing time only because we want to |
1008 | // detect a function started with a label and not ended correctly |
1009 | // without encountering a .functype directive after the label. |
1010 | if (CurrentState != FunctionLabel) { |
1011 | // This .functype indicates a start of a function. |
1012 | if (ensureEmptyNestingStack()) |
1013 | return ParseStatus::Failure; |
1014 | push(NT: Function); |
1015 | } |
1016 | CurrentState = FunctionStart; |
1017 | LastFunctionLabel = WasmSym; |
1018 | } |
1019 | auto *Signature = Ctx.createWasmSignature(); |
1020 | if (parseSignature(Signature)) |
1021 | return ParseStatus::Failure; |
1022 | if (CurrentState == FunctionStart) |
1023 | TC.funcDecl(Sig: *Signature); |
1024 | WasmSym->setSignature(Signature); |
1025 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); |
1026 | TOut.emitFunctionType(Sym: WasmSym); |
1027 | // TODO: backend also calls TOut.emitIndIdx, but that is not implemented. |
1028 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1029 | } |
1030 | |
1031 | if (DirectiveID.getString() == ".export_name" ) { |
1032 | auto SymName = expectIdent(); |
1033 | if (SymName.empty()) |
1034 | return ParseStatus::Failure; |
1035 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
1036 | return ParseStatus::Failure; |
1037 | auto ExportName = expectIdent(); |
1038 | if (ExportName.empty()) |
1039 | return ParseStatus::Failure; |
1040 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
1041 | WasmSym->setExportName(Ctx.allocateString(s: ExportName)); |
1042 | TOut.emitExportName(Sym: WasmSym, ExportName); |
1043 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1044 | } |
1045 | |
1046 | if (DirectiveID.getString() == ".import_module" ) { |
1047 | auto SymName = expectIdent(); |
1048 | if (SymName.empty()) |
1049 | return ParseStatus::Failure; |
1050 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
1051 | return ParseStatus::Failure; |
1052 | auto ImportModule = expectIdent(); |
1053 | if (ImportModule.empty()) |
1054 | return ParseStatus::Failure; |
1055 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
1056 | WasmSym->setImportModule(Ctx.allocateString(s: ImportModule)); |
1057 | TOut.emitImportModule(Sym: WasmSym, ImportModule); |
1058 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1059 | } |
1060 | |
1061 | if (DirectiveID.getString() == ".import_name" ) { |
1062 | auto SymName = expectIdent(); |
1063 | if (SymName.empty()) |
1064 | return ParseStatus::Failure; |
1065 | if (expect(Kind: AsmToken::Comma, KindName: "," )) |
1066 | return ParseStatus::Failure; |
1067 | auto ImportName = expectIdent(); |
1068 | if (ImportName.empty()) |
1069 | return ParseStatus::Failure; |
1070 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
1071 | WasmSym->setImportName(Ctx.allocateString(s: ImportName)); |
1072 | TOut.emitImportName(Sym: WasmSym, ImportName); |
1073 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1074 | } |
1075 | |
1076 | if (DirectiveID.getString() == ".tagtype" ) { |
1077 | auto SymName = expectIdent(); |
1078 | if (SymName.empty()) |
1079 | return ParseStatus::Failure; |
1080 | auto *WasmSym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name: SymName)); |
1081 | auto *Signature = Ctx.createWasmSignature(); |
1082 | if (parseRegTypeList(Types&: Signature->Params)) |
1083 | return ParseStatus::Failure; |
1084 | WasmSym->setSignature(Signature); |
1085 | WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG); |
1086 | TOut.emitTagType(Sym: WasmSym); |
1087 | // TODO: backend also calls TOut.emitIndIdx, but that is not implemented. |
1088 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1089 | } |
1090 | |
1091 | if (DirectiveID.getString() == ".local" ) { |
1092 | if (CurrentState != FunctionStart) |
1093 | return error(Msg: ".local directive should follow the start of a function: " , |
1094 | Tok: Lexer.getTok()); |
1095 | SmallVector<wasm::ValType, 4> Locals; |
1096 | if (parseRegTypeList(Types&: Locals)) |
1097 | return ParseStatus::Failure; |
1098 | TC.localDecl(Locals); |
1099 | TOut.emitLocal(Types: Locals); |
1100 | CurrentState = FunctionLocals; |
1101 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1102 | } |
1103 | |
1104 | if (DirectiveID.getString() == ".int8" || |
1105 | DirectiveID.getString() == ".int16" || |
1106 | DirectiveID.getString() == ".int32" || |
1107 | DirectiveID.getString() == ".int64" ) { |
1108 | if (checkDataSection()) |
1109 | return ParseStatus::Failure; |
1110 | const MCExpr *Val; |
1111 | SMLoc End; |
1112 | if (Parser.parseExpression(Res&: Val, EndLoc&: End)) |
1113 | return error(Msg: "Cannot parse .int expression: " , Tok: Lexer.getTok()); |
1114 | size_t NumBits = 0; |
1115 | DirectiveID.getString().drop_front(N: 4).getAsInteger(Radix: 10, Result&: NumBits); |
1116 | Out.emitValue(Value: Val, Size: NumBits / 8, Loc: End); |
1117 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1118 | } |
1119 | |
1120 | if (DirectiveID.getString() == ".asciz" ) { |
1121 | if (checkDataSection()) |
1122 | return ParseStatus::Failure; |
1123 | std::string S; |
1124 | if (Parser.parseEscapedString(Data&: S)) |
1125 | return error(Msg: "Cannot parse string constant: " , Tok: Lexer.getTok()); |
1126 | Out.emitBytes(Data: StringRef(S.c_str(), S.length() + 1)); |
1127 | return expect(Kind: AsmToken::EndOfStatement, KindName: "EOL" ); |
1128 | } |
1129 | |
1130 | return ParseStatus::NoMatch; // We didn't process this directive. |
1131 | } |
1132 | |
1133 | // Called either when the first instruction is parsed of the function ends. |
1134 | void ensureLocals(MCStreamer &Out) { |
1135 | if (CurrentState == FunctionStart) { |
1136 | // We haven't seen a .local directive yet. The streamer requires locals to |
1137 | // be encoded as a prelude to the instructions, so emit an empty list of |
1138 | // locals here. |
1139 | auto &TOut = reinterpret_cast<WebAssemblyTargetStreamer &>( |
1140 | *Out.getTargetStreamer()); |
1141 | TOut.emitLocal(Types: SmallVector<wasm::ValType, 0>()); |
1142 | CurrentState = FunctionLocals; |
1143 | } |
1144 | } |
1145 | |
1146 | bool matchAndEmitInstruction(SMLoc IDLoc, unsigned & /*Opcode*/, |
1147 | OperandVector &Operands, MCStreamer &Out, |
1148 | uint64_t &ErrorInfo, |
1149 | bool MatchingInlineAsm) override { |
1150 | MCInst Inst; |
1151 | Inst.setLoc(IDLoc); |
1152 | FeatureBitset MissingFeatures; |
1153 | unsigned MatchResult = MatchInstructionImpl( |
1154 | Operands, Inst, ErrorInfo, MissingFeatures, matchingInlineAsm: MatchingInlineAsm); |
1155 | switch (MatchResult) { |
1156 | case Match_Success: { |
1157 | ensureLocals(Out); |
1158 | // Fix unknown p2align operands. |
1159 | auto Align = WebAssembly::GetDefaultP2AlignAny(Opc: Inst.getOpcode()); |
1160 | if (Align != -1U) { |
1161 | auto &Op0 = Inst.getOperand(i: 0); |
1162 | if (Op0.getImm() == -1) |
1163 | Op0.setImm(Align); |
1164 | } |
1165 | if (Is64) { |
1166 | // Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having |
1167 | // an offset64 arg instead of offset32, but to the assembler matcher |
1168 | // they're both immediates so don't get selected for. |
1169 | auto Opc64 = WebAssembly::getWasm64Opcode( |
1170 | Opcode: static_cast<uint16_t>(Inst.getOpcode())); |
1171 | if (Opc64 >= 0) { |
1172 | Inst.setOpcode(Opc64); |
1173 | } |
1174 | } |
1175 | if (!SkipTypeCheck) |
1176 | TC.typeCheck(ErrorLoc: IDLoc, Inst, Operands); |
1177 | Out.emitInstruction(Inst, STI: getSTI()); |
1178 | if (CurrentState == EndFunction) { |
1179 | onEndOfFunction(ErrorLoc: IDLoc); |
1180 | } else { |
1181 | CurrentState = Instructions; |
1182 | } |
1183 | return false; |
1184 | } |
1185 | case Match_MissingFeature: { |
1186 | assert(MissingFeatures.count() > 0 && "Expected missing features" ); |
1187 | SmallString<128> Message; |
1188 | raw_svector_ostream OS(Message); |
1189 | OS << "instruction requires:" ; |
1190 | for (unsigned I = 0, E = MissingFeatures.size(); I != E; ++I) |
1191 | if (MissingFeatures.test(I)) |
1192 | OS << ' ' << getSubtargetFeatureName(Val: I); |
1193 | return Parser.Error(L: IDLoc, Msg: Message); |
1194 | } |
1195 | case Match_MnemonicFail: |
1196 | return Parser.Error(L: IDLoc, Msg: "invalid instruction" ); |
1197 | case Match_NearMisses: |
1198 | return Parser.Error(L: IDLoc, Msg: "ambiguous instruction" ); |
1199 | case Match_InvalidTiedOperand: |
1200 | case Match_InvalidOperand: { |
1201 | SMLoc ErrorLoc = IDLoc; |
1202 | if (ErrorInfo != ~0ULL) { |
1203 | if (ErrorInfo >= Operands.size()) |
1204 | return Parser.Error(L: IDLoc, Msg: "too few operands for instruction" ); |
1205 | ErrorLoc = Operands[ErrorInfo]->getStartLoc(); |
1206 | if (ErrorLoc == SMLoc()) |
1207 | ErrorLoc = IDLoc; |
1208 | } |
1209 | return Parser.Error(L: ErrorLoc, Msg: "invalid operand for instruction" ); |
1210 | } |
1211 | } |
1212 | llvm_unreachable("Implement any new match types added!" ); |
1213 | } |
1214 | |
1215 | void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override { |
1216 | // Code below only applies to labels in text sections. |
1217 | auto *CWS = cast<MCSectionWasm>(Val: getStreamer().getCurrentSectionOnly()); |
1218 | if (!CWS->isText()) |
1219 | return; |
1220 | |
1221 | auto *WasmSym = cast<MCSymbolWasm>(Val: Symbol); |
1222 | // Unlike other targets, we don't allow data in text sections (labels |
1223 | // declared with .type @object). |
1224 | if (WasmSym->getType() == wasm::WASM_SYMBOL_TYPE_DATA) { |
1225 | Parser.Error(L: IDLoc, |
1226 | Msg: "Wasm doesn\'t support data symbols in text sections" ); |
1227 | return; |
1228 | } |
1229 | |
1230 | // Start a new section for the next function automatically, since our |
1231 | // object writer expects each function to have its own section. This way |
1232 | // The user can't forget this "convention". |
1233 | auto SymName = Symbol->getName(); |
1234 | if (SymName.starts_with(Prefix: ".L" )) |
1235 | return; // Local Symbol. |
1236 | |
1237 | // TODO: If the user explicitly creates a new function section, we ignore |
1238 | // its name when we create this one. It would be nice to honor their |
1239 | // choice, while still ensuring that we create one if they forget. |
1240 | // (that requires coordination with WasmAsmParser::parseSectionDirective) |
1241 | std::string SecName = (".text." + SymName).str(); |
1242 | |
1243 | auto *Group = CWS->getGroup(); |
1244 | // If the current section is a COMDAT, also set the flag on the symbol. |
1245 | // TODO: Currently the only place that the symbols' comdat flag matters is |
1246 | // for importing comdat functions. But there's no way to specify that in |
1247 | // assembly currently. |
1248 | if (Group) |
1249 | WasmSym->setComdat(true); |
1250 | auto *WS = getContext().getWasmSection(Section: SecName, K: SectionKind::getText(), Flags: 0, |
1251 | Group, UniqueID: MCSection::NonUniqueID); |
1252 | getStreamer().switchSection(Section: WS); |
1253 | // Also generate DWARF for this section if requested. |
1254 | if (getContext().getGenDwarfForAssembly()) |
1255 | getContext().addGenDwarfSection(Sec: WS); |
1256 | |
1257 | if (WasmSym->isFunction()) { |
1258 | // We give the location of the label (IDLoc) here, because otherwise the |
1259 | // lexer's next location will be used, which can be confusing. For |
1260 | // example: |
1261 | // |
1262 | // test0: ; This function does not end properly |
1263 | // ... |
1264 | // |
1265 | // test1: ; We would like to point to this line for error |
1266 | // ... . Not this line, which can contain any instruction |
1267 | ensureEmptyNestingStack(Loc: IDLoc); |
1268 | CurrentState = FunctionLabel; |
1269 | LastFunctionLabel = Symbol; |
1270 | push(NT: Function); |
1271 | } |
1272 | } |
1273 | |
1274 | void onEndOfFunction(SMLoc ErrorLoc) { |
1275 | if (!SkipTypeCheck) |
1276 | TC.endOfFunction(ErrorLoc, ExactMatch: true); |
1277 | // Reset the type checker state. |
1278 | TC.clear(); |
1279 | } |
1280 | |
1281 | void onEndOfFile() override { ensureEmptyNestingStack(); } |
1282 | }; |
1283 | } // end anonymous namespace |
1284 | |
1285 | // Force static initialization. |
1286 | extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void |
1287 | LLVMInitializeWebAssemblyAsmParser() { |
1288 | RegisterMCAsmParser<WebAssemblyAsmParser> X(getTheWebAssemblyTarget32()); |
1289 | RegisterMCAsmParser<WebAssemblyAsmParser> Y(getTheWebAssemblyTarget64()); |
1290 | } |
1291 | |
1292 | #define GET_REGISTER_MATCHER |
1293 | #define GET_SUBTARGET_FEATURE_NAME |
1294 | #define GET_MATCHER_IMPLEMENTATION |
1295 | #include "WebAssemblyGenAsmMatcher.inc" |
1296 | |
1297 | StringRef getMnemonic(unsigned Opc) { |
1298 | // FIXME: linear search! |
1299 | for (auto &ME : MatchTable0) { |
1300 | if (ME.Opcode == Opc) { |
1301 | return ME.getMnemonic(); |
1302 | } |
1303 | } |
1304 | assert(false && "mnemonic not found" ); |
1305 | return StringRef(); |
1306 | } |
1307 | |