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