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