xref: /llvm-project/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp (revision 43570a2841e2a8f1efd00503beee751cc1e72513)
1 //==- WebAssemblyAsmTypeCheck.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 "llvm/MC/MCContext.h"
22 #include "llvm/MC/MCExpr.h"
23 #include "llvm/MC/MCInst.h"
24 #include "llvm/MC/MCInstrInfo.h"
25 #include "llvm/MC/MCParser/MCParsedAsmOperand.h"
26 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
27 #include "llvm/MC/MCSectionWasm.h"
28 #include "llvm/MC/MCStreamer.h"
29 #include "llvm/MC/MCSubtargetInfo.h"
30 #include "llvm/MC/MCSymbol.h"
31 #include "llvm/MC/MCSymbolWasm.h"
32 #include "llvm/MC/TargetRegistry.h"
33 #include "llvm/Support/Compiler.h"
34 #include "llvm/Support/SourceMgr.h"
35 #include <sstream>
36 
37 using namespace llvm;
38 
39 #define DEBUG_TYPE "wasm-asm-parser"
40 
41 extern StringRef getMnemonic(unsigned Opc);
42 
43 namespace llvm {
44 
45 WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser,
46                                                  const MCInstrInfo &MII,
47                                                  bool Is64)
48     : Parser(Parser), MII(MII), Is64(Is64) {}
49 
50 void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) {
51   LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
52   BlockInfoStack.push_back({Sig, 0, false});
53 }
54 
55 void WebAssemblyAsmTypeCheck::localDecl(
56     const SmallVectorImpl<wasm::ValType> &Locals) {
57   LocalTypes.insert(LocalTypes.end(), Locals.begin(), Locals.end());
58 }
59 
60 void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
61   LLVM_DEBUG({ dbgs() << Msg << getTypesString(Stack) << "\n"; });
62 }
63 
64 bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
65   dumpTypeStack("current stack: ");
66   return Parser.Error(ErrorLoc, Msg);
67 }
68 
69 bool WebAssemblyAsmTypeCheck::match(StackType TypeA, StackType TypeB) {
70   // These should have been filtered out in checkTypes()
71   assert(!std::get_if<Polymorphic>(&TypeA) &&
72          !std::get_if<Polymorphic>(&TypeB));
73 
74   if (TypeA == TypeB)
75     return false;
76   if (std::get_if<Any>(&TypeA) || std::get_if<Any>(&TypeB))
77     return false;
78 
79   if (std::get_if<Ref>(&TypeB))
80     std::swap(TypeA, TypeB);
81   assert(std::get_if<wasm::ValType>(&TypeB));
82   if (std::get_if<Ref>(&TypeA) &&
83       WebAssembly::isRefType(std::get<wasm::ValType>(TypeB)))
84     return false;
85   return true;
86 }
87 
88 std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<StackType> Types,
89                                                     size_t StartPos) {
90   SmallVector<std::string, 4> TypeStrs;
91   for (auto I = Types.size(); I > StartPos; I--) {
92     if (std::get_if<Polymorphic>(&Types[I - 1])) {
93       TypeStrs.push_back("...");
94       break;
95     }
96     if (std::get_if<Any>(&Types[I - 1]))
97       TypeStrs.push_back("any");
98     else if (std::get_if<Ref>(&Types[I - 1]))
99       TypeStrs.push_back("ref");
100     else
101       TypeStrs.push_back(
102           WebAssembly::typeToString(std::get<wasm::ValType>(Types[I - 1])));
103   }
104 
105   std::stringstream SS;
106   SS << "[";
107   bool First = true;
108   for (auto It = TypeStrs.rbegin(); It != TypeStrs.rend(); ++It) {
109     if (!First)
110       SS << ", ";
111     SS << *It;
112     First = false;
113   }
114   SS << "]";
115   return SS.str();
116 }
117 
118 std::string
119 WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<wasm::ValType> Types,
120                                         size_t StartPos) {
121   return getTypesString(valTypesToStackTypes(Types), StartPos);
122 }
123 
124 SmallVector<WebAssemblyAsmTypeCheck::StackType, 4>
125 WebAssemblyAsmTypeCheck::valTypesToStackTypes(
126     ArrayRef<wasm::ValType> ValTypes) {
127   SmallVector<StackType, 4> Types(ValTypes.size());
128   std::transform(ValTypes.begin(), ValTypes.end(), Types.begin(),
129                  [](wasm::ValType Val) -> StackType { return Val; });
130   return Types;
131 }
132 
133 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
134                                          ArrayRef<wasm::ValType> ValTypes,
135                                          bool ExactMatch) {
136   return checkTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
137 }
138 
139 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
140                                          ArrayRef<StackType> Types,
141                                          bool ExactMatch) {
142   auto StackI = Stack.size();
143   auto TypeI = Types.size();
144   assert(!BlockInfoStack.empty());
145   auto BlockStackStartPos = BlockInfoStack.back().StackStartPos;
146   bool Error = false;
147   bool PolymorphicStack = false;
148   // Compare elements one by one from the stack top
149   for (; StackI > BlockStackStartPos && TypeI > 0; StackI--, TypeI--) {
150     // If the stack is polymorphic, we assume all types in 'Types' have been
151     // compared and matched
152     if (std::get_if<Polymorphic>(&Stack[StackI - 1])) {
153       TypeI = 0;
154       break;
155     }
156     if (match(Stack[StackI - 1], Types[TypeI - 1])) {
157       Error = true;
158       break;
159     }
160   }
161 
162   // If the stack top is polymorphic, the stack is in the polymorphic state.
163   if (StackI > BlockStackStartPos &&
164       std::get_if<Polymorphic>(&Stack[StackI - 1]))
165     PolymorphicStack = true;
166 
167   // Even if no match failure has happened in the loop above, if not all
168   // elements of Types has been matched, that means we don't have enough
169   // elements on the stack.
170   //
171   // Also, if not all elements of the Stack has been matched and when
172   // 'ExactMatch' is true and the current stack is not polymorphic, that means
173   // we have superfluous elements remaining on the stack (e.g. at the end of a
174   // function).
175   if (TypeI > 0 ||
176       (ExactMatch && !PolymorphicStack && StackI > BlockStackStartPos))
177     Error = true;
178 
179   if (!Error)
180     return false;
181 
182   auto StackStartPos = ExactMatch
183                            ? BlockStackStartPos
184                            : std::max((int)BlockStackStartPos,
185                                       (int)Stack.size() - (int)Types.size());
186   return typeError(ErrorLoc, "type mismatch, expected " +
187                                  getTypesString(Types) + " but got " +
188                                  getTypesString(Stack, StackStartPos));
189 }
190 
191 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
192                                        ArrayRef<wasm::ValType> ValTypes,
193                                        bool ExactMatch) {
194   return popTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
195 }
196 
197 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
198                                        ArrayRef<StackType> Types,
199                                        bool ExactMatch) {
200   bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
201   auto NumPops = std::min(Stack.size() - BlockInfoStack.back().StackStartPos,
202                           Types.size());
203   for (size_t I = 0, E = NumPops; I != E; I++) {
204     if (std::get_if<Polymorphic>(&Stack.back()))
205       break;
206     Stack.pop_back();
207   }
208   return Error;
209 }
210 
211 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
212   return popTypes(ErrorLoc, {Type});
213 }
214 
215 bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
216   return popType(ErrorLoc, Ref{});
217 }
218 
219 bool WebAssemblyAsmTypeCheck::popAnyType(SMLoc ErrorLoc) {
220   return popType(ErrorLoc, Any{});
221 }
222 
223 void WebAssemblyAsmTypeCheck::pushTypes(ArrayRef<wasm::ValType> ValTypes) {
224   Stack.append(valTypesToStackTypes(ValTypes));
225 }
226 
227 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
228                                        wasm::ValType &Type) {
229   auto Local = static_cast<size_t>(LocalOp.getImm());
230   if (Local >= LocalTypes.size())
231     return typeError(ErrorLoc, StringRef("no local type specified for index ") +
232                                    std::to_string(Local));
233   Type = LocalTypes[Local];
234   return false;
235 }
236 
237 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
238                                        const wasm::WasmSignature &Sig) {
239   bool Error = popTypes(ErrorLoc, Sig.Params);
240   pushTypes(Sig.Returns);
241   return Error;
242 }
243 
244 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
245                                         const MCSymbolRefExpr *&SymRef) {
246   if (!SymOp.isExpr())
247     return typeError(ErrorLoc, StringRef("expected expression operand"));
248   SymRef = dyn_cast<MCSymbolRefExpr>(SymOp.getExpr());
249   if (!SymRef)
250     return typeError(ErrorLoc, StringRef("expected symbol operand"));
251   return false;
252 }
253 
254 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc,
255                                         const MCOperand &GlobalOp,
256                                         wasm::ValType &Type) {
257   const MCSymbolRefExpr *SymRef;
258   if (getSymRef(ErrorLoc, GlobalOp, SymRef))
259     return true;
260   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
261   switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) {
262   case wasm::WASM_SYMBOL_TYPE_GLOBAL:
263     Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
264     break;
265   case wasm::WASM_SYMBOL_TYPE_FUNCTION:
266   case wasm::WASM_SYMBOL_TYPE_DATA:
267     switch (SymRef->getKind()) {
268     case MCSymbolRefExpr::VK_GOT:
269     case MCSymbolRefExpr::VK_WASM_GOT_TLS:
270       Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
271       return false;
272     default:
273       break;
274     }
275     [[fallthrough]];
276   default:
277     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
278                                    ": missing .globaltype");
279   }
280   return false;
281 }
282 
283 bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCOperand &TableOp,
284                                        wasm::ValType &Type) {
285   const MCSymbolRefExpr *SymRef;
286   if (getSymRef(ErrorLoc, TableOp, SymRef))
287     return true;
288   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
289   if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) !=
290       wasm::WASM_SYMBOL_TYPE_TABLE)
291     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
292                                    ": missing .tabletype");
293   Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType);
294   return false;
295 }
296 
297 bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
298                                            const MCOperand &SigOp,
299                                            wasm::WasmSymbolType Type,
300                                            const wasm::WasmSignature *&Sig) {
301   const MCSymbolRefExpr *SymRef = nullptr;
302   if (getSymRef(ErrorLoc, SigOp, SymRef))
303     return true;
304   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
305   Sig = WasmSym->getSignature();
306 
307   if (!Sig || WasmSym->getType() != Type) {
308     const char *TypeName = nullptr;
309     switch (Type) {
310     case wasm::WASM_SYMBOL_TYPE_FUNCTION:
311       TypeName = "func";
312       break;
313     case wasm::WASM_SYMBOL_TYPE_TAG:
314       TypeName = "tag";
315       break;
316     default:
317       llvm_unreachable("Signature symbol should either be a function or a tag");
318     }
319     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
320                                    ": missing ." + TypeName + "type");
321   }
322   return false;
323 }
324 
325 bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
326   assert(!BlockInfoStack.empty());
327   const auto &FuncInfo = BlockInfoStack[0];
328   return checkTypes(ErrorLoc, FuncInfo.Sig.Returns, ExactMatch);
329 }
330 
331 // Unlike checkTypes() family, this just compare the equivalence of the two
332 // ValType vectors
333 static bool compareTypes(ArrayRef<wasm::ValType> TypesA,
334                          ArrayRef<wasm::ValType> TypesB) {
335   if (TypesA.size() != TypesB.size())
336     return true;
337   for (size_t I = 0, E = TypesA.size(); I < E; I++)
338     if (TypesA[I] != TypesB[I])
339       return true;
340   return false;
341 }
342 
343 bool WebAssemblyAsmTypeCheck::checkTryTable(SMLoc ErrorLoc,
344                                             const MCInst &Inst) {
345   bool Error = false;
346   unsigned OpIdx = 1; // OpIdx 0 is the block type
347   int64_t NumCatches = Inst.getOperand(OpIdx++).getImm();
348   for (int64_t I = 0; I < NumCatches; I++) {
349     int64_t Opcode = Inst.getOperand(OpIdx++).getImm();
350     std::string ErrorMsgBase =
351         "try_table: catch index " + std::to_string(I) + ": ";
352 
353     const wasm::WasmSignature *Sig = nullptr;
354     SmallVector<wasm::ValType> SentTypes;
355     if (Opcode == wasm::WASM_OPCODE_CATCH ||
356         Opcode == wasm::WASM_OPCODE_CATCH_REF) {
357       if (!getSignature(ErrorLoc, Inst.getOperand(OpIdx++),
358                         wasm::WASM_SYMBOL_TYPE_TAG, Sig))
359         SentTypes.insert(SentTypes.end(), Sig->Params.begin(),
360                          Sig->Params.end());
361       else
362         Error = true;
363     }
364     if (Opcode == wasm::WASM_OPCODE_CATCH_REF ||
365         Opcode == wasm::WASM_OPCODE_CATCH_ALL_REF) {
366       SentTypes.push_back(wasm::ValType::EXNREF);
367     }
368 
369     unsigned Level = Inst.getOperand(OpIdx++).getImm();
370     if (Level < BlockInfoStack.size()) {
371       const auto &DestBlockInfo =
372           BlockInfoStack[BlockInfoStack.size() - Level - 1];
373       ArrayRef<wasm::ValType> DestTypes;
374       if (DestBlockInfo.IsLoop)
375         DestTypes = DestBlockInfo.Sig.Params;
376       else
377         DestTypes = DestBlockInfo.Sig.Returns;
378       if (compareTypes(SentTypes, DestTypes)) {
379         std::string ErrorMsg =
380             ErrorMsgBase + "type mismatch, catch tag type is " +
381             getTypesString(SentTypes) + ", but destination's type is " +
382             getTypesString(DestTypes);
383         Error |= typeError(ErrorLoc, ErrorMsg);
384       }
385     } else {
386       Error = typeError(ErrorLoc, ErrorMsgBase + "invalid depth " +
387                                       std::to_string(Level));
388     }
389   }
390   return Error;
391 }
392 
393 bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
394                                         OperandVector &Operands) {
395   auto Opc = Inst.getOpcode();
396   auto Name = getMnemonic(Opc);
397   dumpTypeStack("typechecking " + Name + ": ");
398   wasm::ValType Type;
399 
400   if (Name == "local.get") {
401     if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
402       pushType(Type);
403       return false;
404     }
405     pushType(Any{});
406     return true;
407   }
408 
409   if (Name == "local.set") {
410     if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
411       return popType(ErrorLoc, Type);
412     popType(ErrorLoc, Any{});
413     return true;
414   }
415 
416   if (Name == "local.tee") {
417     if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
418       bool Error = popType(ErrorLoc, Type);
419       pushType(Type);
420       return Error;
421     }
422     popType(ErrorLoc, Any{});
423     pushType(Any{});
424     return true;
425   }
426 
427   if (Name == "global.get") {
428     if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
429       pushType(Type);
430       return false;
431     }
432     pushType(Any{});
433     return true;
434   }
435 
436   if (Name == "global.set") {
437     if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
438       return popType(ErrorLoc, Type);
439     popType(ErrorLoc, Any{});
440     return true;
441   }
442 
443   if (Name == "table.get") {
444     bool Error = popType(ErrorLoc, wasm::ValType::I32);
445     if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
446       pushType(Type);
447       return Error;
448     }
449     pushType(Any{});
450     return true;
451   }
452 
453   if (Name == "table.set") {
454     bool Error = false;
455     SmallVector<StackType, 2> PopTypes;
456     PopTypes.push_back(wasm::ValType::I32);
457     if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
458       PopTypes.push_back(Type);
459     } else {
460       Error = true;
461       PopTypes.push_back(Any{});
462     }
463     Error |= popTypes(ErrorLoc, PopTypes);
464     return Error;
465   }
466 
467   if (Name == "table.size") {
468     bool Error = getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type);
469     pushType(wasm::ValType::I32);
470     return Error;
471   }
472 
473   if (Name == "table.grow") {
474     bool Error = false;
475     SmallVector<StackType, 2> PopTypes;
476     if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
477       PopTypes.push_back(Type);
478     } else {
479       Error = true;
480       PopTypes.push_back(Any{});
481     }
482     PopTypes.push_back(wasm::ValType::I32);
483     Error |= popTypes(ErrorLoc, PopTypes);
484     pushType(wasm::ValType::I32);
485     return Error;
486   }
487 
488   if (Name == "table.fill") {
489     bool Error = false;
490     SmallVector<StackType, 2> PopTypes;
491     PopTypes.push_back(wasm::ValType::I32);
492     if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
493       PopTypes.push_back(Type);
494     } else {
495       Error = true;
496       PopTypes.push_back(Any{});
497     }
498     PopTypes.push_back(wasm::ValType::I32);
499     Error |= popTypes(ErrorLoc, PopTypes);
500     return Error;
501   }
502 
503   if (Name == "memory.fill") {
504     Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
505     bool Error = popType(ErrorLoc, Type);
506     Error |= popType(ErrorLoc, wasm::ValType::I32);
507     Error |= popType(ErrorLoc, Type);
508     return Error;
509   }
510 
511   if (Name == "memory.copy") {
512     Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
513     bool Error = popType(ErrorLoc, Type);
514     Error |= popType(ErrorLoc, Type);
515     Error |= popType(ErrorLoc, Type);
516     return Error;
517   }
518 
519   if (Name == "memory.init") {
520     Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
521     bool Error = popType(ErrorLoc, wasm::ValType::I32);
522     Error |= popType(ErrorLoc, wasm::ValType::I32);
523     Error |= popType(ErrorLoc, Type);
524     return Error;
525   }
526 
527   if (Name == "drop") {
528     return popType(ErrorLoc, Any{});
529   }
530 
531   if (Name == "block" || Name == "loop" || Name == "if" || Name == "try" ||
532       Name == "try_table") {
533     bool Error = Name == "if" && popType(ErrorLoc, wasm::ValType::I32);
534     // Pop block input parameters and check their types are correct
535     Error |= popTypes(ErrorLoc, LastSig.Params);
536     if (Name == "try_table")
537       Error |= checkTryTable(ErrorLoc, Inst);
538     // Push a new block info
539     BlockInfoStack.push_back({LastSig, Stack.size(), Name == "loop"});
540     // Push back block input parameters
541     pushTypes(LastSig.Params);
542     return Error;
543   }
544 
545   if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
546       Name == "end_try" || Name == "delegate" || Name == "end_try_table" ||
547       Name == "else" || Name == "catch" || Name == "catch_all") {
548     assert(!BlockInfoStack.empty());
549     // Check if the types on the stack match with the block return type
550     const auto &LastBlockInfo = BlockInfoStack.back();
551     bool Error = checkTypes(ErrorLoc, LastBlockInfo.Sig.Returns, true);
552     // Pop all types added to the stack for the current block level
553     Stack.truncate(LastBlockInfo.StackStartPos);
554     if (Name == "else") {
555       // 'else' expects the block input parameters to be on the stack, in the
556       // same way we entered 'if'
557       pushTypes(LastBlockInfo.Sig.Params);
558     } else if (Name == "catch") {
559       // 'catch' instruction pushes values whose types are specified in the
560       // tag's 'params' part
561       const wasm::WasmSignature *Sig = nullptr;
562       if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
563                         wasm::WASM_SYMBOL_TYPE_TAG, Sig))
564         pushTypes(Sig->Params);
565       else
566         Error = true;
567     } else if (Name == "catch_all") {
568       // 'catch_all' does not push anything onto the stack
569     } else {
570       // For normal end markers, push block return value types onto the stack
571       // and pop the block info
572       pushTypes(LastBlockInfo.Sig.Returns);
573       BlockInfoStack.pop_back();
574     }
575     return Error;
576   }
577 
578   if (Name == "br" || Name == "br_if") {
579     bool Error = false;
580     if (Name == "br_if")
581       Error |= popType(ErrorLoc, wasm::ValType::I32); // cond
582     const MCOperand &Operand = Inst.getOperand(0);
583     if (Operand.isImm()) {
584       unsigned Level = Operand.getImm();
585       if (Level < BlockInfoStack.size()) {
586         const auto &DestBlockInfo =
587             BlockInfoStack[BlockInfoStack.size() - Level - 1];
588         if (DestBlockInfo.IsLoop)
589           Error |= checkTypes(ErrorLoc, DestBlockInfo.Sig.Params, false);
590         else
591           Error |= checkTypes(ErrorLoc, DestBlockInfo.Sig.Returns, false);
592       } else {
593         Error = typeError(ErrorLoc, StringRef("br: invalid depth ") +
594                                         std::to_string(Level));
595       }
596     } else {
597       Error =
598           typeError(Operands[1]->getStartLoc(), "depth should be an integer");
599     }
600     if (Name == "br")
601       pushType(Polymorphic{});
602     return Error;
603   }
604 
605   if (Name == "return") {
606     bool Error = endOfFunction(ErrorLoc, false);
607     pushType(Polymorphic{});
608     return Error;
609   }
610 
611   if (Name == "call_indirect" || Name == "return_call_indirect") {
612     // Function value.
613     bool Error = popType(ErrorLoc, wasm::ValType::I32);
614     Error |= checkSig(ErrorLoc, LastSig);
615     if (Name == "return_call_indirect") {
616       Error |= endOfFunction(ErrorLoc, false);
617       pushType(Polymorphic{});
618     }
619     return Error;
620   }
621 
622   if (Name == "call" || Name == "return_call") {
623     bool Error = false;
624     const wasm::WasmSignature *Sig = nullptr;
625     if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
626                       wasm::WASM_SYMBOL_TYPE_FUNCTION, Sig))
627       Error |= checkSig(ErrorLoc, *Sig);
628     else
629       Error = true;
630     if (Name == "return_call") {
631       Error |= endOfFunction(ErrorLoc, false);
632       pushType(Polymorphic{});
633     }
634     return Error;
635   }
636 
637   if (Name == "unreachable") {
638     pushType(Polymorphic{});
639     return false;
640   }
641 
642   if (Name == "ref.is_null") {
643     bool Error = popRefType(ErrorLoc);
644     pushType(wasm::ValType::I32);
645     return Error;
646   }
647 
648   if (Name == "throw") {
649     bool Error = false;
650     const wasm::WasmSignature *Sig = nullptr;
651     if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
652                       wasm::WASM_SYMBOL_TYPE_TAG, Sig))
653       Error |= checkSig(ErrorLoc, *Sig);
654     else
655       Error = true;
656     pushType(Polymorphic{});
657     return Error;
658   }
659 
660   if (Name == "throw_ref") {
661     bool Error = popType(ErrorLoc, wasm::ValType::EXNREF);
662     pushType(Polymorphic{});
663     return Error;
664   }
665 
666   // The current instruction is a stack instruction which doesn't have
667   // explicit operands that indicate push/pop types, so we get those from
668   // the register version of the same instruction.
669   auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
670   assert(RegOpc != -1 && "Failed to get register version of MC instruction");
671   const auto &II = MII.get(RegOpc);
672   // First pop all the uses off the stack and check them.
673   SmallVector<wasm::ValType, 4> PopTypes;
674   for (unsigned I = II.getNumDefs(); I < II.getNumOperands(); I++) {
675     const auto &Op = II.operands()[I];
676     if (Op.OperandType == MCOI::OPERAND_REGISTER)
677       PopTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
678   }
679   bool Error = popTypes(ErrorLoc, PopTypes);
680   SmallVector<wasm::ValType, 4> PushTypes;
681   // Now push all the defs onto the stack.
682   for (unsigned I = 0; I < II.getNumDefs(); I++) {
683     const auto &Op = II.operands()[I];
684     assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
685     PushTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
686   }
687   pushTypes(PushTypes);
688   return Error;
689 }
690 
691 } // end namespace llvm
692