xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/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/MCParsedAsmOperand.h"
27 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
28 #include "llvm/MC/MCSectionWasm.h"
29 #include "llvm/MC/MCStreamer.h"
30 #include "llvm/MC/MCSubtargetInfo.h"
31 #include "llvm/MC/MCSymbol.h"
32 #include "llvm/MC/MCSymbolWasm.h"
33 #include "llvm/MC/TargetRegistry.h"
34 #include "llvm/Support/Compiler.h"
35 #include "llvm/Support/SourceMgr.h"
36 #include <sstream>
37 
38 using namespace llvm;
39 
40 #define DEBUG_TYPE "wasm-asm-parser"
41 
42 extern StringRef getMnemonic(unsigned Opc);
43 
44 namespace llvm {
45 
WebAssemblyAsmTypeCheck(MCAsmParser & Parser,const MCInstrInfo & MII,bool Is64)46 WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser,
47                                                  const MCInstrInfo &MII,
48                                                  bool Is64)
49     : Parser(Parser), MII(MII), Is64(Is64) {}
50 
funcDecl(const wasm::WasmSignature & Sig)51 void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) {
52   LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
53   BlockInfoStack.push_back({Sig, 0, false});
54 }
55 
localDecl(const SmallVectorImpl<wasm::ValType> & Locals)56 void WebAssemblyAsmTypeCheck::localDecl(
57     const SmallVectorImpl<wasm::ValType> &Locals) {
58   llvm::append_range(LocalTypes, Locals);
59 }
60 
dumpTypeStack(Twine Msg)61 void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
62   LLVM_DEBUG({ dbgs() << Msg << getTypesString(Stack) << "\n"; });
63 }
64 
typeError(SMLoc ErrorLoc,const Twine & Msg)65 bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
66   dumpTypeStack("current stack: ");
67   return Parser.Error(ErrorLoc, Msg);
68 }
69 
match(StackType TypeA,StackType TypeB)70 bool WebAssemblyAsmTypeCheck::match(StackType TypeA, StackType TypeB) {
71   // These should have been filtered out in checkTypes()
72   assert(!std::get_if<Polymorphic>(&TypeA) &&
73          !std::get_if<Polymorphic>(&TypeB));
74 
75   if (TypeA == TypeB)
76     return false;
77   if (std::get_if<Any>(&TypeA) || std::get_if<Any>(&TypeB))
78     return false;
79 
80   if (std::get_if<Ref>(&TypeB))
81     std::swap(TypeA, TypeB);
82   assert(std::get_if<wasm::ValType>(&TypeB));
83   if (std::get_if<Ref>(&TypeA) &&
84       WebAssembly::isRefType(std::get<wasm::ValType>(TypeB)))
85     return false;
86   return true;
87 }
88 
getTypesString(ArrayRef<StackType> Types,size_t StartPos)89 std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<StackType> Types,
90                                                     size_t StartPos) {
91   SmallVector<std::string, 4> TypeStrs;
92   for (auto I = Types.size(); I > StartPos; I--) {
93     if (std::get_if<Polymorphic>(&Types[I - 1])) {
94       TypeStrs.push_back("...");
95       break;
96     }
97     if (std::get_if<Any>(&Types[I - 1]))
98       TypeStrs.push_back("any");
99     else if (std::get_if<Ref>(&Types[I - 1]))
100       TypeStrs.push_back("ref");
101     else
102       TypeStrs.push_back(
103           WebAssembly::typeToString(std::get<wasm::ValType>(Types[I - 1])));
104   }
105 
106   std::stringstream SS;
107   SS << "[";
108   bool First = true;
109   for (auto It = TypeStrs.rbegin(); It != TypeStrs.rend(); ++It) {
110     if (!First)
111       SS << ", ";
112     SS << *It;
113     First = false;
114   }
115   SS << "]";
116   return SS.str();
117 }
118 
119 std::string
getTypesString(ArrayRef<wasm::ValType> Types,size_t StartPos)120 WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<wasm::ValType> Types,
121                                         size_t StartPos) {
122   return getTypesString(valTypesToStackTypes(Types), StartPos);
123 }
124 
125 SmallVector<WebAssemblyAsmTypeCheck::StackType, 4>
valTypesToStackTypes(ArrayRef<wasm::ValType> ValTypes)126 WebAssemblyAsmTypeCheck::valTypesToStackTypes(
127     ArrayRef<wasm::ValType> ValTypes) {
128   SmallVector<StackType, 4> Types(ValTypes.size());
129   llvm::transform(ValTypes, Types.begin(),
130                   [](wasm::ValType Val) -> StackType { return Val; });
131   return Types;
132 }
133 
checkTypes(SMLoc ErrorLoc,ArrayRef<wasm::ValType> ValTypes,bool ExactMatch)134 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
135                                          ArrayRef<wasm::ValType> ValTypes,
136                                          bool ExactMatch) {
137   return checkTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
138 }
139 
checkTypes(SMLoc ErrorLoc,ArrayRef<StackType> Types,bool ExactMatch)140 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
141                                          ArrayRef<StackType> Types,
142                                          bool ExactMatch) {
143   auto StackI = Stack.size();
144   auto TypeI = Types.size();
145   assert(!BlockInfoStack.empty());
146   auto BlockStackStartPos = BlockInfoStack.back().StackStartPos;
147   bool Error = false;
148   bool PolymorphicStack = false;
149   // Compare elements one by one from the stack top
150   for (; StackI > BlockStackStartPos && TypeI > 0; StackI--, TypeI--) {
151     // If the stack is polymorphic, we assume all types in 'Types' have been
152     // compared and matched
153     if (std::get_if<Polymorphic>(&Stack[StackI - 1])) {
154       TypeI = 0;
155       break;
156     }
157     if (match(Stack[StackI - 1], Types[TypeI - 1])) {
158       Error = true;
159       break;
160     }
161   }
162 
163   // If the stack top is polymorphic, the stack is in the polymorphic state.
164   if (StackI > BlockStackStartPos &&
165       std::get_if<Polymorphic>(&Stack[StackI - 1]))
166     PolymorphicStack = true;
167 
168   // Even if no match failure has happened in the loop above, if not all
169   // elements of Types has been matched, that means we don't have enough
170   // elements on the stack.
171   //
172   // Also, if not all elements of the Stack has been matched and when
173   // 'ExactMatch' is true and the current stack is not polymorphic, that means
174   // we have superfluous elements remaining on the stack (e.g. at the end of a
175   // function).
176   if (TypeI > 0 ||
177       (ExactMatch && !PolymorphicStack && StackI > BlockStackStartPos))
178     Error = true;
179 
180   if (!Error)
181     return false;
182 
183   auto StackStartPos = ExactMatch
184                            ? BlockStackStartPos
185                            : std::max((int)BlockStackStartPos,
186                                       (int)Stack.size() - (int)Types.size());
187   return typeError(ErrorLoc, "type mismatch, expected " +
188                                  getTypesString(Types) + " but got " +
189                                  getTypesString(Stack, StackStartPos));
190 }
191 
popTypes(SMLoc ErrorLoc,ArrayRef<wasm::ValType> ValTypes,bool ExactMatch)192 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
193                                        ArrayRef<wasm::ValType> ValTypes,
194                                        bool ExactMatch) {
195   return popTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
196 }
197 
popTypes(SMLoc ErrorLoc,ArrayRef<StackType> Types,bool ExactMatch)198 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
199                                        ArrayRef<StackType> Types,
200                                        bool ExactMatch) {
201   bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
202   auto NumPops = std::min(Stack.size() - BlockInfoStack.back().StackStartPos,
203                           Types.size());
204   for (size_t I = 0, E = NumPops; I != E; I++) {
205     if (std::get_if<Polymorphic>(&Stack.back()))
206       break;
207     Stack.pop_back();
208   }
209   return Error;
210 }
211 
popType(SMLoc ErrorLoc,StackType Type)212 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
213   return popTypes(ErrorLoc, {Type});
214 }
215 
popRefType(SMLoc ErrorLoc)216 bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
217   return popType(ErrorLoc, Ref{});
218 }
219 
popAnyType(SMLoc ErrorLoc)220 bool WebAssemblyAsmTypeCheck::popAnyType(SMLoc ErrorLoc) {
221   return popType(ErrorLoc, Any{});
222 }
223 
pushTypes(ArrayRef<wasm::ValType> ValTypes)224 void WebAssemblyAsmTypeCheck::pushTypes(ArrayRef<wasm::ValType> ValTypes) {
225   Stack.append(valTypesToStackTypes(ValTypes));
226 }
227 
getLocal(SMLoc ErrorLoc,const MCOperand & LocalOp,wasm::ValType & Type)228 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
229                                        wasm::ValType &Type) {
230   auto Local = static_cast<size_t>(LocalOp.getImm());
231   if (Local >= LocalTypes.size())
232     return typeError(ErrorLoc, StringRef("no local type specified for index ") +
233                                    std::to_string(Local));
234   Type = LocalTypes[Local];
235   return false;
236 }
237 
checkSig(SMLoc ErrorLoc,const wasm::WasmSignature & Sig)238 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
239                                        const wasm::WasmSignature &Sig) {
240   bool Error = popTypes(ErrorLoc, Sig.Params);
241   pushTypes(Sig.Returns);
242   return Error;
243 }
244 
getSymRef(SMLoc ErrorLoc,const MCOperand & SymOp,const MCSymbolRefExpr * & SymRef)245 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
246                                         const MCSymbolRefExpr *&SymRef) {
247   if (!SymOp.isExpr())
248     return typeError(ErrorLoc, StringRef("expected expression operand"));
249   SymRef = dyn_cast<MCSymbolRefExpr>(SymOp.getExpr());
250   if (!SymRef)
251     return typeError(ErrorLoc, StringRef("expected symbol operand"));
252   return false;
253 }
254 
getGlobal(SMLoc ErrorLoc,const MCOperand & GlobalOp,wasm::ValType & Type)255 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc,
256                                         const MCOperand &GlobalOp,
257                                         wasm::ValType &Type) {
258   const MCSymbolRefExpr *SymRef;
259   if (getSymRef(ErrorLoc, GlobalOp, SymRef))
260     return true;
261   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
262   switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) {
263   case wasm::WASM_SYMBOL_TYPE_GLOBAL:
264     Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
265     break;
266   case wasm::WASM_SYMBOL_TYPE_FUNCTION:
267   case wasm::WASM_SYMBOL_TYPE_DATA:
268     switch (SymRef->getSpecifier()) {
269     case WebAssembly::S_GOT:
270     case WebAssembly::S_GOT_TLS:
271       Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
272       return false;
273     default:
274       break;
275     }
276     [[fallthrough]];
277   default:
278     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
279                                    ": missing .globaltype");
280   }
281   return false;
282 }
283 
getTable(SMLoc ErrorLoc,const MCOperand & TableOp,wasm::ValType & Type)284 bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCOperand &TableOp,
285                                        wasm::ValType &Type) {
286   const MCSymbolRefExpr *SymRef;
287   if (getSymRef(ErrorLoc, TableOp, SymRef))
288     return true;
289   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
290   if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) !=
291       wasm::WASM_SYMBOL_TYPE_TABLE)
292     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
293                                    ": missing .tabletype");
294   Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType);
295   return false;
296 }
297 
getSignature(SMLoc ErrorLoc,const MCOperand & SigOp,wasm::WasmSymbolType Type,const wasm::WasmSignature * & Sig)298 bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
299                                            const MCOperand &SigOp,
300                                            wasm::WasmSymbolType Type,
301                                            const wasm::WasmSignature *&Sig) {
302   const MCSymbolRefExpr *SymRef = nullptr;
303   if (getSymRef(ErrorLoc, SigOp, SymRef))
304     return true;
305   const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
306   Sig = WasmSym->getSignature();
307 
308   if (!Sig || WasmSym->getType() != Type) {
309     const char *TypeName = nullptr;
310     switch (Type) {
311     case wasm::WASM_SYMBOL_TYPE_FUNCTION:
312       TypeName = "func";
313       break;
314     case wasm::WASM_SYMBOL_TYPE_TAG:
315       TypeName = "tag";
316       break;
317     default:
318       llvm_unreachable("Signature symbol should either be a function or a tag");
319     }
320     return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
321                                    ": missing ." + TypeName + "type");
322   }
323   return false;
324 }
325 
endOfFunction(SMLoc ErrorLoc,bool ExactMatch)326 bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
327   assert(!BlockInfoStack.empty());
328   const auto &FuncInfo = BlockInfoStack[0];
329   return checkTypes(ErrorLoc, FuncInfo.Sig.Returns, ExactMatch);
330 }
331 
332 // Unlike checkTypes() family, this just compare the equivalence of the two
333 // ValType vectors
compareTypes(ArrayRef<wasm::ValType> TypesA,ArrayRef<wasm::ValType> TypesB)334 static bool compareTypes(ArrayRef<wasm::ValType> TypesA,
335                          ArrayRef<wasm::ValType> TypesB) {
336   if (TypesA.size() != TypesB.size())
337     return true;
338   for (size_t I = 0, E = TypesA.size(); I < E; I++)
339     if (TypesA[I] != TypesB[I])
340       return true;
341   return false;
342 }
343 
checkTryTable(SMLoc ErrorLoc,const MCInst & Inst)344 bool WebAssemblyAsmTypeCheck::checkTryTable(SMLoc ErrorLoc,
345                                             const MCInst &Inst) {
346   bool Error = false;
347   unsigned OpIdx = 1; // OpIdx 0 is the block type
348   int64_t NumCatches = Inst.getOperand(OpIdx++).getImm();
349   for (int64_t I = 0; I < NumCatches; I++) {
350     int64_t Opcode = Inst.getOperand(OpIdx++).getImm();
351     std::string ErrorMsgBase =
352         "try_table: catch index " + std::to_string(I) + ": ";
353 
354     const wasm::WasmSignature *Sig = nullptr;
355     SmallVector<wasm::ValType> SentTypes;
356     if (Opcode == wasm::WASM_OPCODE_CATCH ||
357         Opcode == wasm::WASM_OPCODE_CATCH_REF) {
358       if (!getSignature(ErrorLoc, Inst.getOperand(OpIdx++),
359                         wasm::WASM_SYMBOL_TYPE_TAG, Sig))
360         llvm::append_range(SentTypes, Sig->Params);
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 
typeCheck(SMLoc ErrorLoc,const MCInst & Inst,OperandVector & Operands)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